// Copyright 2021, OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // OpAMP: Open Agent Management Protocol (https://github.com/open-telemetry/opamp-spec) syntax = "proto3"; package opamp.proto; import "anyvalue.proto"; option go_package = "github.com/open-telemetry/opamp-go/protobufs"; message AgentToServer { // Globally unique identifier of the running instance of the Agent. SHOULD remain // unchanged for the lifetime of the Agent process. // Recommended format: https://github.com/ulid/spec string instance_uid = 1; // The sequence number is incremented by 1 for every AgentToServer sent // by the Agent. This allows the Server to detect that it missed a message when // it notices that the sequence_num is not exactly by 1 greater than the previously // received one. uint64 sequence_num = 2; // Data that describes the Agent, its type, where it runs, etc. // May be omitted if nothing changed since last AgentToServer message. AgentDescription agent_description = 3; // Bitmask of flags defined by AgentCapabilities enum. // All bits that are not defined in AgentCapabilities enum MUST be set to 0 by // the Agent. This allows extending the protocol and the AgentCapabilities enum // in the future such that old Agents automatically report that they don't // support the new capability. // This field MUST be always set. uint64 capabilities = 4; // The current health of the Agent. // May be omitted if nothing changed since last AgentToServer message. AgentHealth health = 5; // The current effective configuration of the Agent. The effective configuration is // the one that is currently used by the Agent. The effective configuration may be // different from the remote configuration received from the Server earlier, e.g. // because the Agent uses a local configuration instead (or in addition). // // This field SHOULD be unset if the effective config is unchanged since the last // AgentToServer message. EffectiveConfig effective_config = 6; // The status of the remote config that was previously received from the Server. // This field SHOULD be unset if the remote config status is unchanged since the // last AgentToServer message. RemoteConfigStatus remote_config_status = 7; // The list of the Agent packages, including package statuses. This field SHOULD be // unset if this information is unchanged since the last AgentToServer message for // this Agent was sent in the stream. PackageStatuses package_statuses = 8; // AgentDisconnect MUST be set in the last AgentToServer message sent from the // Agent to the Server. AgentDisconnect agent_disconnect = 9; // Bit flags as defined by AgentToServerFlags bit masks. uint64 flags = 10; } enum AgentToServerFlags { AgentToServerFlags_Unspecified = 0; // AgentToServerFlags 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 AgentToServerFlags_RequestInstanceUid = 0x00000001; } // AgentDisconnect is the last message sent from the Agent to the Server. The Server // SHOULD forget the association of the Agent instance with the message stream. // // If the message stream is closed in the transport layer then the Server SHOULD // forget association of all Agent instances that were previously established for // this message stream using AgentConnect message, even if the corresponding // AgentDisconnect message were not explicitly received from the Agent. message AgentDisconnect { } message ServerToAgent { // Agent instance uid. MUST match the instance_uid field in AgentToServer message. // Used for multiplexing messages from/to multiple agents using one message stream. string instance_uid = 1; // error_response is set if the Server wants to indicate that something went wrong // during processing of an AgentToServer message. If error_response is set then // all other fields below must be unset and vice versa, if any of the fields below is // set then error_response must be unset. ServerErrorResponse error_response = 2; // remote_config field is set when the Server has a remote config offer for the Agent. AgentRemoteConfig remote_config = 3; // This field is set when the Server wants the Agent to change one or more // of its client connection settings (destination, headers, certificate, etc). ConnectionSettingsOffers connection_settings = 4; // This field is set when the Server has packages to offer to the Agent. PackagesAvailable packages_available = 5; // Bit flags as defined by ServerToAgentFlags bit masks. uint64 flags = 6; // Bitmask of flags defined by ServerCapabilities enum. // All bits that are not defined in ServerCapabilities enum MUST be set to 0 // by the Server. This allows extending the protocol and the ServerCapabilities // enum in the future such that old Servers automatically report that they // don't support the new capability. // This field MUST be set in the first ServerToAgent sent by the Server and MAY // be omitted in subsequent ServerToAgent messages by setting it to // UnspecifiedServerCapability value. uint64 capabilities = 7; // Properties related to identification of the Agent, which can be overridden // by the Server if needed. AgentIdentification agent_identification = 8; // Allows the Server to instruct the Agent to perform a command, e.g. RESTART. This field should not be specified // with fields other than instance_uid and capabilities. If specified, other fields will be ignored and the command // will be performed. ServerToAgentCommand command = 9; } enum ServerToAgentFlags { ServerToAgentFlags_Unspecified = 0; // Flags is a bit mask. Values below define individual bits. // ReportFullState flag can be used by the Server if the Agent did not include the // particular bit of information in the last status report (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 full status. ServerToAgentFlags_ReportFullState = 0x00000001; } enum ServerCapabilities { // The capabilities field is unspecified. ServerCapabilities_Unspecified = 0; // The Server can accept status reports. This bit MUST be set, since all Server // MUST be able to accept status reports. ServerCapabilities_AcceptsStatus = 0x00000001; // The Server can offer remote configuration to the Agent. ServerCapabilities_OffersRemoteConfig = 0x00000002; // The Server can accept EffectiveConfig in AgentToServer. ServerCapabilities_AcceptsEffectiveConfig = 0x00000004; // The Server can offer Packages. ServerCapabilities_OffersPackages = 0x00000008; // The Server can accept Packages status. ServerCapabilities_AcceptsPackagesStatus = 0x00000010; // The Server can offer connection settings. ServerCapabilities_OffersConnectionSettings = 0x00000020; // Add new capabilities here, continuing with the least significant unused bit. } // The OpAMPConnectionSettings message is a collection of fields which comprise an // offer from the Server to the Agent to use the specified settings for OpAMP // connection. message OpAMPConnectionSettings { // OpAMP Server URL This MUST be a WebSocket or HTTP URL and MUST be non-empty, for // example: "wss://example.com:4318/v1/opamp" string destination_endpoint = 1; // Optional headers to use when connecting. Typically used to set access tokens or // other authorization headers. For HTTP-based protocols the Agent should // set these in the request headers. // For example: // key="Authorization", Value="Basic YWxhZGRpbjpvcGVuc2VzYW1l". Headers headers = 2; // The Agent should use the offered certificate to connect to the destination // from now on. If the Agent is able to validate and connect using the offered // certificate the Agent SHOULD forget any previous client certificates // for this connection. // This field is optional: if omitted the client SHOULD NOT use a client-side certificate. // This field can be used to perform a client certificate revocation/rotation. TLSCertificate certificate = 3; } // The TelemetryConnectionSettings message is a collection of fields which comprise an // offer from the Server to the Agent to use the specified settings for a network // connection to report own telemetry. message TelemetryConnectionSettings { // The value MUST be a full URL an OTLP/HTTP/Protobuf receiver with path. Schema // SHOULD begin with "https://", for example "https://example.com:4318/v1/metrics" // The Agent MAY refuse to send the telemetry if the URL begins with "http://". string destination_endpoint = 1; // Optional headers to use when connecting. Typically used to set access tokens or // other authorization headers. For HTTP-based protocols the Agent should // set these in the request headers. // For example: // key="Authorization", Value="Basic YWxhZGRpbjpvcGVuc2VzYW1l". Headers headers = 2; // The Agent should use the offered certificate to connect to the destination // from now on. If the Agent is able to validate and connect using the offered // certificate the Agent SHOULD forget any previous client certificates // for this connection. // This field is optional: if omitted the client SHOULD NOT use a client-side certificate. // This field can be used to perform a client certificate revocation/rotation. TLSCertificate certificate = 3; } // The OtherConnectionSettings message is a collection of fields which comprise an // offer from the Server to the Agent to use the specified settings for a network // connection. It is not required that all fields in this message are specified. // The Server may specify only some of the fields, in which case it means that // the Server offers the Agent to change only those fields, while keeping the // rest of the fields unchanged. // // For example the Server may send a ConnectionSettings message with only the // certificate field set, while all other fields are unset. This means that // the Server wants the Agent to use a new certificate and continue sending to // the destination it is currently sending using the current header and other // settings. // // For fields which reference other messages the field is considered unset // when the reference is unset. // // For primitive field (string) we rely on the "flags" to describe that the // field is not set (this is done to overcome the limitation of old protoc // compilers don't generate methods that allow to check for the presence of // the field. message OtherConnectionSettings { // A URL, host:port or some other destination specifier. string destination_endpoint = 1; // Optional headers to use when connecting. Typically used to set access tokens or // other authorization headers. For HTTP-based protocols the Agent should // set these in the request headers. // For example: // key="Authorization", Value="Basic YWxhZGRpbjpvcGVuc2VzYW1l". Headers headers = 2; // The Agent should use the offered certificate to connect to the destination // from now on. If the Agent is able to validate and connect using the offered // certificate the Agent SHOULD forget any previous client certificates // for this connection. // This field is optional: if omitted the client SHOULD NOT use a client-side certificate. // This field can be used to perform a client certificate revocation/rotation. TLSCertificate certificate = 3; // Other connection settings. These are Agent-specific and are up to the Agent // interpret. map other_settings = 4; } message Headers { repeated Header headers = 1; } message Header { string key = 1; string value = 2; } message TLSCertificate { // The (public_key,private_key) certificate pair should be issued and // signed by a Certificate Authority that the destination Server recognizes. // // It is highly recommended that the private key of the CA certificate is NOT // stored on the destination Server otherwise compromising the Server will allow // a malicious actor to issue valid Server certificates which will be automatically // trusted by all agents and will allow the actor to trivially MITM Agent-to-Server // traffic of all servers that use this CA certificate for their Server-side // certificates. // // Alternatively the certificate may be self-signed, assuming the Server can // verify the certificate. // PEM-encoded public key of the certificate. Required. bytes public_key = 1; // PEM-encoded private key of the certificate. Required. bytes private_key = 2; // PEM-encoded public key of the CA that signed this certificate. // Optional. MUST be specified if the certificate is CA-signed. // Can be stored by TLS-terminating intermediary proxies in order to verify // the connecting client's certificate in the future. // It is not recommended that the Agent accepts this CA as an authority for // any purposes. bytes ca_public_key = 3; } message ConnectionSettingsOffers { // Hash of all settings, including settings that may be omitted from this message // because they are unchanged. bytes hash = 1; // Settings to connect to the OpAMP Server. // If this field is not set then the Agent should assume that the settings are // unchanged and should continue using existing settings. // The Agent MUST verify the offered connection settings by actually connecting // before accepting the setting to ensure it does not loose access to the OpAMP // Server due to invalid settings. OpAMPConnectionSettings opamp = 2; // Settings to connect to an OTLP metrics backend to send Agent's own metrics to. // If this field is not set then the Agent should assume that the settings // are unchanged. // // Once accepted the Agent should periodically send to the specified destination // its own metrics, i.e. metrics of the Agent process and any custom metrics that // describe the Agent state. // // All attributes specified in the identifying_attributes field in AgentDescription // message SHOULD be also specified in the Resource of the reported OTLP metrics. // // Attributes specified in the non_identifying_attributes field in // AgentDescription message may be also specified in the Resource of the reported // OTLP metrics, in which case they SHOULD have exactly the same values. // // Process metrics MUST follow the conventions for processes: // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/process-metrics.md TelemetryConnectionSettings own_metrics = 3; // Similar to own_metrics, but for traces. TelemetryConnectionSettings own_traces = 4; // Similar to own_metrics, but for logs. TelemetryConnectionSettings own_logs = 5; // Another set of connection settings, with a string name associated with each. // How the Agent uses these is Agent-specific. Typically the name represents // the name of the destination to connect to (as it is known to the Agent). // If this field is not set then the Agent should assume that the other_connections // settings are unchanged. map other_connections = 6; } // List of packages that the Server offers to the Agent. message PackagesAvailable { // Map of packages. Keys are package names, values are the packages available for download. map packages = 1; // Aggregate hash of all remotely installed packages. The Agent SHOULD include this // value in subsequent PackageStatuses messages. This in turn allows the management // Server to identify that a different set of packages is available for the Agent // and specify the available packages in the next ServerToAgent message. // // This field MUST be always set if the management Server supports packages // of agents. // // The hash is calculated as an aggregate of all packages names and content. bytes all_packages_hash = 2; } // Each Agent is composed of one or more packages. A package has a name and // content stored in a file. The content of the files, functionality // provided by the packages, how they are stored and used by the Agent side is Agent // type-specific and is outside the concerns of the OpAMP protocol. // // If the Agent does not have an installed package with the specified name then // it SHOULD download it from the specified URL and install it. // // If the Agent already has an installed package with the specified name // but with a different hash then the Agent SHOULD download and // install the package again, since it is a different version of the same package. // // If the Agent has an installed package with the specified name and the same // hash then the Agent does not need to do anything, it already // has the right version of the package. message PackageAvailable { PackageType type = 1; // The package version that is available on the Server side. The Agent may for // example use this information to avoid downloading a package that was previously // already downloaded and failed to install. string version = 2; // The downloadable file of the package. DownloadableFile file = 3; // The hash of the package. SHOULD be calculated based on all other fields of the // PackageAvailable message and content of the file of the package. The hash is // used by the Agent to determine if the package it has is different from the // package the Server is offering. bytes hash = 4; } // The type of the package, either an addon or a top-level package. enum PackageType { PackageType_TopLevel = 0; PackageType_Addon = 1; } message DownloadableFile { // The URL from which the file can be downloaded using HTTP GET request. // The Server at the specified URL SHOULD support range requests // to allow for resuming downloads. string download_url = 1; // The hash of the file content. Can be used by the Agent to verify that the file // was downloaded correctly. bytes content_hash = 2; // Optional signature of the file content. Can be used by the Agent to verify the // authenticity of the downloaded file, for example can be the // [detached GPG signature](https://www.gnupg.org/gph/en/manual/x135.html#AEN160). // The exact signing and verification method is Agent specific. See // https://github.com/open-telemetry/opamp-spec/blob/main/specification.md#code-signing // for recommendations. bytes signature = 3; } message ServerErrorResponse { ServerErrorResponseType type = 1; // Error message in the string form, typically human readable. string error_message = 2; oneof Details { // Additional information about retrying if type==UNAVAILABLE. RetryInfo retry_info = 3; } } enum ServerErrorResponseType { // Unknown error. Something went wrong, but it is not known what exactly. // The Agent SHOULD NOT retry the message. // The error_message field may contain a description of the problem. ServerErrorResponseType_Unknown = 0; // The AgentToServer message was malformed. The Agent SHOULD NOT retry // the message. ServerErrorResponseType_BadRequest = 1; // The Server is overloaded and unable to process the request. The Agent // should retry the message later. retry_info field may be optionally // set with additional information about retrying. ServerErrorResponseType_Unavailable = 2; } message RetryInfo { uint64 retry_after_nanoseconds = 1; } // ServerToAgentCommand is sent from the Server to the Agent to request that the Agent // perform a command. message ServerToAgentCommand { CommandType type = 1; } enum CommandType { // The Agent should restart. This request will be ignored if the Agent does not // support restart. CommandType_Restart = 0; } //////////////////////////////////////////////////////////////////////////////////// // Status reporting message AgentDescription { // Attributes that identify the Agent. // Keys/values are according to OpenTelemetry semantic conventions, see: // https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/resource/semantic_conventions // // For standalone running Agents (such as OpenTelemetry Collector) the following // attributes SHOULD be specified: // - service.name should be set to a reverse FQDN that uniquely identifies the // Agent type, e.g. "io.opentelemetry.collector" // - service.namespace if it is used in the environment where the Agent runs. // - service.version should be set to version number of the Agent build. // - service.instance.id should be set. It may be be set equal to the Agent's // instance uid (equal to ServerToAgent.instance_uid field) or any other value // that uniquely identifies the Agent in combination with other attributes. // - any other attributes that are necessary for uniquely identifying the Agent's // own telemetry. // // The Agent SHOULD also include these attributes in the Resource of its own // telemetry. The combination of identifying attributes SHOULD be sufficient to // uniquely identify the Agent's own telemetry in the destination system to which // the Agent sends its own telemetry. repeated KeyValue identifying_attributes = 1; // Attributes that do not necessarily identify the Agent but help describe // where it runs. // The following attributes SHOULD be included: // - os.type, os.version - to describe where the Agent runs. // - host.* to describe the host the Agent runs on. // - cloud.* to describe the cloud where the host is located. // - any other relevant Resource attributes that describe this Agent and the // environment it runs in. // - any user-defined attributes that the end user would like to associate // with this Agent. repeated KeyValue non_identifying_attributes = 2; // TODO: add ability to specify related entities (such as the Service the Agent is // is responsible/associated with). } enum AgentCapabilities { // The capabilities field is unspecified. AgentCapabilities_Unspecified = 0; // The Agent can report status. This bit MUST be set, since all Agents MUST // report status. AgentCapabilities_ReportsStatus = 0x00000001; // The Agent can accept remote configuration from the Server. AgentCapabilities_AcceptsRemoteConfig = 0x00000002; // The Agent will report EffectiveConfig in AgentToServer. AgentCapabilities_ReportsEffectiveConfig = 0x00000004; // The Agent can accept package offers. AgentCapabilities_AcceptsPackages = 0x00000008; // The Agent can report package status. AgentCapabilities_ReportsPackageStatuses = 0x00000010; // The Agent can report own trace to the destination specified by // the Server via ConnectionSettingsOffers.own_traces field. AgentCapabilities_ReportsOwnTraces = 0x00000020; // The Agent can report own metrics to the destination specified by // the Server via ConnectionSettingsOffers.own_metrics field. AgentCapabilities_ReportsOwnMetrics = 0x00000040; // The Agent can report own logs to the destination specified by // the Server via ConnectionSettingsOffers.own_logs field. AgentCapabilities_ReportsOwnLogs = 0x00000080; // The can accept connections settings for OpAMP via // ConnectionSettingsOffers.opamp field. AgentCapabilities_AcceptsOpAMPConnectionSettings = 0x00000100; // The can accept connections settings for other destinations via // ConnectionSettingsOffers.other_connections field. AgentCapabilities_AcceptsOtherConnectionSettings = 0x00000200; // The Agent can accept restart requests. AgentCapabilities_AcceptsRestartCommand = 0x00000400; // The Agent will report Health via AgentToServer.health field. AgentCapabilities_ReportsHealth = 0x00000800; // The Agent will report RemoteConfig status via AgentToServer.remote_config_status field. AgentCapabilities_ReportsRemoteConfig = 0x00001000; // Add new capabilities here, continuing with the least significant unused bit. } // The health of the Agent. message AgentHealth { // Set to true if the Agent is up and healthy. bool healthy = 1; // Timestamp since the Agent is up, i.e. when the agent was started. // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. // If the agent is not running MUST be set to 0. fixed64 start_time_unix_nano = 2; // Human-readable error message if the Agent is in erroneous state. SHOULD be set // when healthy==false. string last_error = 3; } message EffectiveConfig { // The effective config of the Agent. AgentConfigMap config_map = 1; } message RemoteConfigStatus { // The hash of the remote config that was last received by this Agent in the // AgentRemoteConfig.config_hash field. // The Server SHOULD compare this hash with the config hash // it has for the Agent and if the hashes are different the Server MUST include // the remote_config field in the response in the ServerToAgent message. bytes last_remote_config_hash = 1; RemoteConfigStatuses status = 2; // Optional error message if status==FAILED. string error_message = 3; } enum RemoteConfigStatuses { // The value of status field is not set. RemoteConfigStatuses_UNSET = 0; // Remote config was successfully applied by the Agent. RemoteConfigStatuses_APPLIED = 1; // Agent is currently applying the remote config that it received earlier. RemoteConfigStatuses_APPLYING = 2; // Agent tried to apply the config received earlier, but it failed. // See error_message for more details. RemoteConfigStatuses_FAILED = 3; } // The PackageStatuses message describes the status of all packages that the Agent // has or was offered. message PackageStatuses { // A map of PackageStatus messages, where the keys are package names. // The key MUST match the name field of PackageStatus message. map packages = 1; // The aggregate hash of all packages that this Agent previously received from the // Server via PackagesAvailable message. // // The Server SHOULD compare this hash to the aggregate hash of all packages that // it has for this Agent and if the hashes are different the Server SHOULD send // an PackagesAvailable message to the Agent. bytes server_provided_all_packages_hash = 2; // This field is set if the Agent encountered an error when processing the // PackagesAvailable message and that error is not related to any particular single // package. // The field must be unset is there were no processing errors. string error_message = 3; } // The status of a single package. message PackageStatus { // Package name. MUST be always set and MUST match the key in the packages field // of PackageStatuses message. string name = 1; // The version of the package that the Agent has. // MUST be set if the Agent has this package. // MUST be empty if the Agent does not have this package. This may be the case // for example if the package was offered by the Server but failed to install // and the Agent did not have this package previously. string agent_has_version = 2; // The hash of the package that the Agent has. // MUST be set if the Agent has this package. // MUST be empty if the Agent does not have this package. This may be the case for // example if the package was offered by the Server but failed to install and the // Agent did not have this package previously. bytes agent_has_hash = 3; // The version of the package that the Server offered to the Agent. // MUST be set if the installation of the package is initiated by an earlier offer // from the Server to install this package. // // MUST be empty if the Agent has this package but it was installed locally and // was not offered by the Server. // // Note that it is possible for both agent_has_version and server_offered_version // fields to be set and to have different values. This is for example possible if // the Agent already has a version of the package successfully installed, the Server // offers a different version, but the Agent fails to install that version. string server_offered_version = 4; // The hash of the package that the Server offered to the Agent. // MUST be set if the installation of the package is initiated by an earlier // offer from the Server to install this package. // // MUST be empty if the Agent has this package but it was installed locally and // was not offered by the Server. // // Note that it is possible for both agent_has_hash and server_offered_hash // fields to be set and to have different values. This is for example possible if // the Agent already has a version of the package successfully installed, the // Server offers a different version, but the Agent fails to install that version. bytes server_offered_hash = 5; PackageStatusEnum status = 6; // Error message if the status is erroneous. string error_message = 7; } // The status of this package. enum PackageStatusEnum { // Package is successfully installed by the Agent. // The error_message field MUST NOT be set. PackageStatusEnum_Installed = 0; // Installation of this package has not yet started. PackageStatusEnum_InstallPending = 1; // Agent is currently downloading and installing the package. // server_offered_hash field MUST be set to indicate the version that the // Agent is installing. The error_message field MUST NOT be set. PackageStatusEnum_Installing = 2; // Agent tried to install the package but installation failed. // server_offered_hash field MUST be set to indicate the version that the Agent // tried to install. The error_message may also contain more details about // the failure. PackageStatusEnum_InstallFailed = 3; } // Properties related to identification of the Agent, which can be overridden // by the Server if needed message AgentIdentification { // When new_instance_uid is set, Agent MUST update instance_uid // to the value provided and use it for all further communication. string new_instance_uid = 1; } ///////////////////////////////////////////////////////////////////////////////////// // Config messages ///////////////////////////////////////////////////////////////////////////////////// message AgentRemoteConfig { // Agent config offered by the management Server to the Agent instance. SHOULD NOT be // set if the config for this Agent has not changed since it was last requested (i.e. // AgentConfigRequest.last_remote_config_hash field is equal to // AgentConfigResponse.config_hash field). AgentConfigMap config = 1; // Hash of "config". The Agent SHOULD include this value in subsequent // RemoteConfigStatus messages in the last_remote_config_hash field. This in turn // allows the management Server to identify that a new config is available for the Agent. // // This field MUST be always set if the management Server supports remote configuration // of agents. // // Management Server must choose a hashing function that guarantees lack of hash // collisions in practice. bytes config_hash = 2; } message AgentConfigMap { // Map of configs. Keys are config file names or config section names. // The configuration is assumed to be a collection of one or more named config files // or sections. // For agents that use a single config file or section the map SHOULD contain a single // entry and the key may be an empty string. map config_map = 1; } message AgentConfigFile { // Config file or section body. The content, format and encoding depends on the Agent // type. The content_type field may optionally describe the MIME type of the body. bytes body = 1; // Optional MIME Content-Type that describes what's in the body field, for // example "text/yaml". string content_type = 2; }