diff --git a/object/service.proto b/object/service.proto
index 1168e4e..f2154a0 100644
--- a/object/service.proto
+++ b/object/service.proto
@@ -283,6 +283,45 @@ service ObjectService {
// - **TOKEN_EXPIRED** (4097, SECTION_SESSION): \
// provided session token has expired.
rpc PutSingle(PutSingleRequest) returns (PutSingleResponse);
+
+ // Patch the object. Request uses gRPC stream. First message must set
+ // the address of the object that is going to be patched. If the object's attributes
+ // are patched, then these attrubutes must be set only within the first stream message.
+ //
+ // If the patch request is performed by NOT the object's owner but if the actor has the permission
+ // to perform the patch, then `OwnerID` of the object is changed. In this case the object's owner
+ // loses the object's ownership after the patch request is successfully done.
+ //
+ // Extended headers can change `Patch` behaviour:
+ // * [ __SYSTEM__NETMAP_EPOCH \
+ // (`__NEOFS__NETMAP_EPOCH` is deprecated) \
+ // Will use the requsted version of Network Map for object placement
+ // calculation.
+ //
+ // Please refer to detailed `XHeader` description.
+ //
+ // Statuses:
+ // - **OK** (0, SECTION_SUCCESS): \
+ // object has been successfully patched and saved in the container;
+ // - Common failures (SECTION_FAILURE_COMMON);
+ // - **ACCESS_DENIED** (2048, SECTION_OBJECT): \
+ // write access to the container is denied;
+ // - **LOCKED** (2050, SECTION_OBJECT): \
+ // placement of an object of type TOMBSTONE that includes at least one
+ // locked object is prohibited;
+ // - **LOCK_NON_REGULAR_OBJECT** (2051, SECTION_OBJECT): \
+ // placement of an object of type LOCK that includes at least one object of
+ // type other than REGULAR is prohibited;
+ // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \
+ // object storage container not found;
+ // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \
+ // access to container is denied;
+ // - **TOKEN_NOT_FOUND** (4096, SECTION_SESSION): \
+ // (for trusted object preparation) session private key does not exist or
+ // has been deleted;
+ // - **TOKEN_EXPIRED** (4097, SECTION_SESSION): \
+ // provided session token has expired.
+ rpc Patch(stream PatchRequest) returns (PatchResponse);
}
// GET object request
@@ -816,4 +855,70 @@ message PutSingleResponse {
// authenticate the nodes of the message route and check the correctness of
// transmission.
neo.fs.v2.session.ResponseVerificationHeader verify_header = 3;
-}
\ No newline at end of file
+}
+
+// Object PATCH request
+message PatchRequest {
+ // PATCH request body
+ message Body {
+ // The address of the object that is requested to get patched.
+ neo.fs.v2.refs.Address address = 1;
+
+ // New attributes for the object. See `replace_attributes` flag usage to define how
+ // new attributes should be set.
+ repeated neo.fs.v2.object.Header.Attribute new_attributes = 2;
+
+ // If this flag is set, then the object's attributes will be entirely replaced by `new_attributes` list.
+ // The empty `new_attributes` list with `replace_attributes = true` just resets attributes list for the object.
+ //
+ // Default `false` value for this flag means the attributes will be just merged. If the incoming `new_attributes`
+ // list contains already existing key, then it just replaces it while merging the lists.
+ bool replace_attributes = 3;
+
+ // The patch for the object's payload.
+ message Patch {
+ // The range of the source object for which the payload is replaced by the patch's chunk.
+ // If the range's `length = 0`, then the patch's chunk is just appended to the original payload
+ // starting from the `offest` without any replace.
+ Range source_range = 1;
+
+ // The chunk that is being appended to or that replaces the original payload on the given range.
+ bytes chunk = 2;
+ }
+
+ // The patch that is applied for the object.
+ Patch patch = 4;
+ }
+
+ // Body for patch request message.
+ Body body = 1;
+
+ // Carries request meta information. Header data is used only to regulate
+ // message transport and does not affect request execution.
+ neo.fs.v2.session.RequestMetaHeader meta_header = 2;
+
+ // Carries request verification information. This header is used to
+ // authenticate the nodes of the message route and check the correctness of
+ // transmission.
+ neo.fs.v2.session.RequestVerificationHeader verify_header = 3;
+}
+
+// Object PATCH response
+message PatchResponse {
+ // PATCH response body
+ message Body {
+ // The object's ID for which the patch has been applied.
+ neo.fs.v2.refs.ObjectID object_id = 1;
+ }
+
+ // Body for patch response message.
+ Body body = 1;
+
+ // Carries response meta information. Header data is used only to regulate
+ // message transport and does not affect request execution.
+ neo.fs.v2.session.ResponseMetaHeader meta_header = 2;
+
+ // Carries response verification information. This header is used to authenticate
+ // the nodes of the message route and check the correctness of transmission.
+ neo.fs.v2.session.ResponseVerificationHeader verify_header = 3;
+}
diff --git a/proto-docs/object.md b/proto-docs/object.md
index c141a60..23fcbc2 100644
--- a/proto-docs/object.md
+++ b/proto-docs/object.md
@@ -30,6 +30,13 @@
- [HeadResponse](#neo.fs.v2.object.HeadResponse)
- [HeadResponse.Body](#neo.fs.v2.object.HeadResponse.Body)
- [HeaderWithSignature](#neo.fs.v2.object.HeaderWithSignature)
+ - [PatchRequest](#neo.fs.v2.object.PatchRequest)
+ - [PatchRequest.Body](#neo.fs.v2.object.PatchRequest.Body)
+ - [PatchRequest.Body.AttributePatch](#neo.fs.v2.object.PatchRequest.Body.AttributePatch)
+ - [PatchRequest.Body.Init](#neo.fs.v2.object.PatchRequest.Body.Init)
+ - [PatchRequest.Body.PayloadPatch](#neo.fs.v2.object.PatchRequest.Body.PayloadPatch)
+ - [PatchResponse](#neo.fs.v2.object.PatchResponse)
+ - [PatchResponse.Body](#neo.fs.v2.object.PatchResponse.Body)
- [PutRequest](#neo.fs.v2.object.PutRequest)
- [PutRequest.Body](#neo.fs.v2.object.PutRequest.Body)
- [PutRequest.Body.Init](#neo.fs.v2.object.PutRequest.Body.Init)
@@ -691,6 +698,95 @@ following steps:
| signature | [neo.fs.v2.refs.Signature](#neo.fs.v2.refs.Signature) | | Signed `ObjectID` to verify full header's authenticity |
+
+
+### Message PatchRequest
+Object PATCH request
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| body | [PatchRequest.Body](#neo.fs.v2.object.PatchRequest.Body) | | Body for patch request message. |
+| meta_header | [neo.fs.v2.session.RequestMetaHeader](#neo.fs.v2.session.RequestMetaHeader) | | Carries request meta information. Header data is used only to regulate message transport and does not affect request execution. |
+| verify_header | [neo.fs.v2.session.RequestVerificationHeader](#neo.fs.v2.session.RequestVerificationHeader) | | Carries request verification information. This header is used to authenticate the nodes of the message route and check the correctness of transmission. |
+
+
+
+
+### Message PatchRequest.Body
+PATCH request body
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| init | [PatchRequest.Body.Init](#neo.fs.v2.object.PatchRequest.Body.Init) | | Initial part of the patch stream. |
+| attr_patch | [PatchRequest.Body.AttributePatch](#neo.fs.v2.object.PatchRequest.Body.AttributePatch) | | The patch for attributes. |
+| payload_patch | [PatchRequest.Body.PayloadPatch](#neo.fs.v2.object.PatchRequest.Body.PayloadPatch) | | The patch for the object's payload. |
+
+
+
+
+### Message PatchRequest.Body.AttributePatch
+The patch for attributes.
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| new_attributes | [Header.Attribute](#neo.fs.v2.object.Header.Attribute) | repeated | New attributes for the object. See `replace_attributes` flag usage to define how new attributes should be set. |
+| replace_attributes | [bool](#bool) | | If this flag is set, then the object's attributes will be entirely replaced by `new_attributes` list. The empty `new_attributes` list with `replace_attributes = true` just resets attributes list for the object.
+
+Default `false` value for this flag means the attributes will be just merged. If the incoming `new_attributes` list contains already existing key, then it just replaces it while merging the lists. |
+
+
+
+
+### Message PatchRequest.Body.Init
+Initial part of the patch stream. This part doesn't refer to any patch but only
+to specific info about the object for that patches are going to be applied.
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| object_id | [neo.fs.v2.refs.ObjectID](#neo.fs.v2.refs.ObjectID) | | The object's ID for which the patch is applied. |
+| header | [Header](#neo.fs.v2.object.Header) | | Object's Header |
+
+
+
+
+### Message PatchRequest.Body.PayloadPatch
+The patch for the object's payload.
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| source_range | [Range](#neo.fs.v2.object.Range) | | The range of the source object for which the payload is replaced by the patch's chunk. If the range's `length = 0`, then the patch's chunk is just appended to the original payload starting from the `offest` without any replace. |
+| chunk | [bytes](#bytes) | | The payload that is being appended to or that replaces the original payload on the given range. |
+
+
+
+
+### Message PatchResponse
+Object PATCH response
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| body | [PatchResponse.Body](#neo.fs.v2.object.PatchResponse.Body) | | Body for patch response message. |
+| meta_header | [neo.fs.v2.session.ResponseMetaHeader](#neo.fs.v2.session.ResponseMetaHeader) | | Carries response meta information. Header data is used only to regulate message transport and does not affect request execution. |
+| verify_header | [neo.fs.v2.session.ResponseVerificationHeader](#neo.fs.v2.session.ResponseVerificationHeader) | | Carries response verification information. This header is used to authenticate the nodes of the message route and check the correctness of transmission. |
+
+
+
+
+### Message PatchResponse.Body
+PATCH response body
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| object_id | [neo.fs.v2.refs.ObjectID](#neo.fs.v2.refs.ObjectID) | | The object's ID for which the patch has been applied. |
+
+
### Message PutRequest