diff --git a/doc/image/filter_illustration.svg b/doc/image/filter_illustration.svg
new file mode 100644
index 0000000..a57aa68
--- /dev/null
+++ b/doc/image/filter_illustration.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/doc/image/placement_policy.svg b/doc/image/placement_policy.svg
new file mode 100644
index 0000000..9cc8a1a
--- /dev/null
+++ b/doc/image/placement_policy.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/doc/image/rep_illustration.svg b/doc/image/rep_illustration.svg
new file mode 100644
index 0000000..8bcc327
--- /dev/null
+++ b/doc/image/rep_illustration.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/doc/image/sample_netmap.svg b/doc/image/sample_netmap.svg
new file mode 100644
index 0000000..3d0b39f
--- /dev/null
+++ b/doc/image/sample_netmap.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/doc/image/select_illustration.svg b/doc/image/select_illustration.svg
new file mode 100644
index 0000000..3a0956a
--- /dev/null
+++ b/doc/image/select_illustration.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/doc/policy.md b/doc/policy.md
new file mode 100644
index 0000000..6818468
--- /dev/null
+++ b/doc/policy.md
@@ -0,0 +1,445 @@
+# Placement Policy
+
+This document describes placement policies, their purpose, syntax and semantics.
+
+## Index
+
+- [Introduction](#introduction)
+- [Operations](#operations)
+ - [Basic Expressions](#basic-expressions)
+ - [`FILTER`](#filter)
+ - [`SELECT`](#select)
+ - [`REP`](#rep)
+- [Policies](#policies)
+ - [The policy playground](#the-policy-playground)
+ - [`CBF`](#cbf)
+ - [`UNIQUE`](#unique)
+ - [More examples](#more-examples)
+- [Appendix 1: Operators](#appendix-1-operators)
+- [Appendix 2: Policy playground commands](#appendix-2-policy-playground-commands)
+
+## Introduction
+
+The purpose of a **placement policy** is to determine whichs node(s) of a frostfs system will store an object. Namely, given a **netmap** (a set of nodes) and a placement policy, a subset of those nodes is selected to store a given object. An important aspect is that since nodes in a netmap come and go due to distributed nature of the system, this selection must be deterministic and consistent, i.e. different nodes must come to the same conclusion as long as they share the same view of the netmap.
+
+> ℹ️ Throughout this document, we will consider each node as a dictionary of attributes and a global unique ID.
+
+One way to think about the placement policy, is as a pipeline of operations which begins with a set of nodes (the entire netmap) and gradually refine it into only the nodes that will be in charge of storing the object. More specifically, each operation in this pipeline takes a set of nodes and transforms it into a subset of those nodes. The transformation is done purely on the basis of the node attributes.
+
+![Placement policy as a pipeline](./image/placement_policy.svg)
+
+The three main operations are:
+1. `FILTER`: filters a set of nodes based on their attributes.
+2. `SELECT`: selects a specific amount of nodes from a set of nodes based on certain conditions.
+3. `REP`: specifies how many nodes (and which ones) from a set of nodes are used to store an object.
+
+In the next sections, we will explore each of them in detail.
+
+## Operations
+
+### Basic Expressions
+
+Before exploring the operations in detail, we must get acquainted with the basic expressions that appear in a placement policy. As mentioned above, the placement policy operates solely on the basis of node attributes, and as such, basic expressions mostly revolve around node attribute comparison.
+
+A comparison expression expresses whether a node attribute equals a specified value:
+```
+AttributeName Operation AttributeValue
+```
+
+For example, the following expression
+```sql
+City EQ 'Moscow'
+```
+asserts that the node attribute `City` equals the value `Moscow`.
+
+Comparison expressions can be nested via boolean operators and parentheses. For example, the following expression:
+```sql
+(City EQ 'Moscow') AND (Disks GT 2)
+```
+asserts that the node attribute `City` equals the value `Moscow` and the node attribute `Disks` must be greater than `2`. Note that the arguments can be either a string or a number.
+
+See [Appendix 1](#appendix-1-operators) for a complete list of supported operators.
+
+### `FILTER`
+
+A `FILTER` operation takes as input a set of nodes and returns a subset of those nodes. It's useful for selecting nodes that have (or lack) specific attributes. Its basic syntax is as follows:
+```bnf
+FILTER AS
+```
+
+For example, the following filter
+```sql
+FILTER Color EQ 'Red' AS RedNodes
+```
+selects those nodes for which the `Color` attribute equals `Red`, and discards the rest. The filter's identifier is `RedNodes`, which can be used to reference it in other parts of the placement policy. For example, you could reference the above filter in another filter as follows
+```sql
+FILTER @RedNodes AND (City EQ 'Moscow') AS RedMoscowNodes
+```
+which would select those nodes for which the `Color` attribute equals `Red` and the `City` attribute equals `Moscow`. You can think of the `@` operator as embedding the referenced filter expression verbatim where it's used. This makes it easy to compose filters. However, filters can be referenced via `@` only within filter expressions. In other places you can simply use the filter identifier directly.
+
+> ⚠️ Every filter requires a unique identifier. What would be the use of a filter that you cannot reference?
+
+The following diagram illustrates the filter operation
+
+![FILTER](./image/filter_illustration.svg)
+
+where the nodes are represented as colored circles, with their color representing the value of their `Color` attribute, respectively.
+
+> ℹ️ A filter referring to all nodes in the netmap always exists and can be referenced by `*`.
+
+### `SELECT`
+
+A `SELECT` operation specifies how many and which nodes from a subset previously obtained from a `FILTER` will be available to build replica groups for object storage. It's not that different from a `FILTER` in that it transforms a set of nodes into a subset of those, but while a `FILTER` cannot control the size of the resulting subset and other characteristics, a `SELECT` can.
+
+Its basic syntax is as follows:
+```bnf
+SELECT {IN (SAME|DISTINCT) } FROM {AS }
+```
+
+In a nutshell, a `SELECT` takes a filter result as input and outputs a specific number of nodes, optionally enforcing that all output nodes must either share or differ in a specific attribute. Note that only the output node count and the source filter are required.
+
+Let's see some examples
+```sql
+-- Selects exactly one node from the entire netmap
+SELECT 1 FROM *
+
+-- Same as above, but with an identifier for the selection
+SELECT 1 FROM * AS ONE
+
+-- Selects two nodes from the RedOrBlueNodes filter, such that both selected nodes
+-- share the same value for the Color attribute, i.e. both red or both blue.
+SELECT 2 IN SAME Color FROM RedOrBlueNodes
+
+-- Selects two nodes from the RedOrBlueNodes filter, such that the selected nodes
+-- have distinct values for the Color attribute, i.e. one red and one blue.
+-- The selection is also given an identifier.
+SELECT 2 IN DISTINCT Color FROM RedOrBlueNodes AS MyNodes
+```
+
+The last example is illustrated in the following diagram:
+
+![SELECT](./image/select_illustration.svg)
+
+> ℹ️ At this point, notice that while `FILTER`'s output is always unique (namely, every node in the input is either filtered in or out), that is not always the case for `SELECT`. In the last example above, there is more than one way to select two nodes with distinct `Color` attribute. Because we require the output to be deterministic and consistent (so that all nodes agree on which nodes to store a given object without having to commmunicate with each other), we need a way to reach this consensus efficiently. Internally, the policy engine uses [Rendezvouz Hashing](https://en.wikipedia.org/wiki/Rendezvous_hashing) to ensure this. If you want more control over what nodes are actually selected, you can always use narrower filters/selections to ensure this.
+
+### `REP`
+
+A `REP` operation specifies how many copies of an object need to be stored (`REP` stands for "replica"). A placement policy can contain multiple replica operations, with each of them representing a replica group, i.e. a group of objects associated with the same replica. Following our analogy with a pipeline, `REP` operations are the sink or output nodes.
+
+Its basic syntax is as follows:
+```bnf
+REP {IN