This is an automated email from the ASF dual-hosted git repository.

jerryshao pushed a commit to branch branch-1.3
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/branch-1.3 by this push:
     new 4c2faaa97c [Cherry-pick to branch-1.3] [MINOR] Split tags and policies 
into concept and API pages (#12326) (#12362)
4c2faaa97c is described below

commit 4c2faaa97cd6a79e27f11b484180126e33feb1a8
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Wed Aug 5 17:22:09 2026 +0800

    [Cherry-pick to branch-1.3] [MINOR] Split tags and policies into concept 
and API pages (#12326) (#12362)
    
    **Cherry-pick Information:**
    - Original commit: 4f19f1371811ee09327a02b437eeed52ce7fe934
    - Target branch: `branch-1.3`
    - Status: ⚠️ **Has conflicts - manual resolution required**
    
    **Do not merge** until conflict markers are resolved and the
    `cherry-pick-conflict` label is removed.
    
    Please review and resolve the conflicts before merging.
    
    Co-authored-by: Mark Hoerth <[email protected]>
    Co-authored-by: Mark Hoerth <[email protected]>
    Co-authored-by: Jerry Shao <[email protected]>
    Co-authored-by: Jerry Shao <[email protected]>
---
 docs/manage-policies-in-gravitino.md | 350 ++++++++++-------------------------
 docs/manage-tags-in-gravitino.md     | 303 ++++++++++++++----------------
 docs/policies.md                     | 187 +++++++++++++++++++
 docs/tags.md                         | 152 +++++++++++++++
 4 files changed, 581 insertions(+), 411 deletions(-)

diff --git a/docs/manage-policies-in-gravitino.md 
b/docs/manage-policies-in-gravitino.md
index 98f923483e..f9e60ea4e5 100644
--- a/docs/manage-policies-in-gravitino.md
+++ b/docs/manage-policies-in-gravitino.md
@@ -1,7 +1,6 @@
 ---
 title: "Manage Policies"
 slug: "/manage-policies-in-gravitino"
-date: 2025-08-04
 keyword: "policy management, policy, policies, Gravitino, data governance"
 license: "This software is licensed under the Apache License version 2."
 ---
@@ -11,74 +10,33 @@ import TabItem from '@theme/TabItem';
 
 ## Introduction
 
-Gravitino provides a policy system that allows you to manage policies for
-metadata objects. Policies are a set of rules that can be associated with a 
metadata
-object for data governance and similar purposes.
-
-This document provides a brief introduction to using policies in Gravitino, 
covering both the Gravitino Java client and 
-REST APIs. If you want to know more about the policy system in Gravitino, 
refer to the
-Javadoc and REST API documentation.
-
-:::info
-1. Metadata objects are objects that are managed in Gravitino, such as 
`CATALOG`, `SCHEMA`, `TABLE`,
-   `FILESET`, `TOPIC`, and `MODEL`. A metadata object is combined by a `type` 
and a dot-separated
-   `name`. For example, a `CATALOG` object has a name "catalog1" with type 
"CATALOG", a `SCHEMA`
-   object has a name "catalog1.schema1" with type "SCHEMA", a `TABLE` object 
has a name
-   "catalog1.schema1.table1" with type "TABLE".
-2`CATALOG`, `SCHEMA`, `TABLE`, `FILESET`, `TOPIC`, and `MODEL` objects can be
-   associated with policies. 
-3. Policies in Gravitino are inheritable, so listing policies of a metadata 
object will also list the
-   policies of its parent metadata objects. For example, listing policies of a 
`Table` will also list
-   the policies of its parent `Schema` and `Catalog`. For catalogs that 
support multi-level
-   (hierarchical) schemas, such as a schema named `a:b:c` (using the 
configured schema separator),
-   the intermediate parent schemas `a:b` and `a` are also part of the 
hierarchy, so their policies
-   are inherited as well.
-4. The same policy can be associated with both parent and child metadata 
objects. But when you list the
-   associated policies of a child metadata object, this policy will be 
included only once in the result
-   list with `inherited` value `false`.
-:::
+This page covers the Gravitino API for policies. For what a policy is, which 
object types can carry one, what goes in
+policy content, how inheritance resolves, and how to work with policies in the 
UI, see
+[Policies](./policies.md).
 
-## Policy Operations
-
-### Create New Policies
+The Python client does not cover policies, so the examples below are REST and 
Java only.
 
-The first step to managing policies is to create new policies. Create a policy 
by providing a
-name, type, and other optional fields like comment, enabled, etc.
+## Policy Operations
 
-Gravitino supports two kinds of policies: built-in policies and custom 
policies.
-For built-in policies, the `policyType` starts with `system_` and the 
`supportedObjectTypes` in the policy content is predefined.
-For custom policies, the `policyType` must be `custom` and the 
`supportedObjectTypes` can be any combination of metadata object types.
+### Create a Policy
 
-:::note
-1. The field `supportedObjectTypes` in the content is immutable after the 
policy is created.
-:::
+A policy needs a name and a type. Content carries the rules, the object types 
the policy supports,
+and optional properties. `supportedObjectTypes` cannot be changed after 
creation.
 
 <Tabs groupId='language' queryString>
-<TabItem value="shell" label="Shell">
+<TabItem value="shell" label="REST">
 
 ```shell
-# Create a custom policy
 curl -X POST -H "Accept: application/vnd.gravitino.v1+json" \
--H "Content-Type: application/json" -d '{
-  "name": "my_policy1",
-  "comment": "This is a test policy",
+  -H "Content-Type: application/json" -d '{
+  "name": "retention_30d",
+  "comment": "Thirty day retention",
   "policyType": "custom",
   "enabled": true,
   "content": {
-    "customRules": {
-      "rule1": 123
-    },
-    "supportedObjectTypes": [
-      "CATALOG",
-      "SCHEMA",
-      "TABLE",
-      "FILESET",
-      "TOPIC",
-      "MODEL"
-    ],
-    "properties": {
-      "key1": "value1"
-    }
+    "customRules": {"retentionDays": 30},
+    "supportedObjectTypes": ["CATALOG", "SCHEMA", "TABLE"],
+    "properties": {"owner": "platform"}
   }
 }' http://localhost:8090/api/metalakes/test/policies
 ```
@@ -87,194 +45,149 @@ curl -X POST -H "Accept: 
application/vnd.gravitino.v1+json" \
 <TabItem value="java" label="Java">
 
 ```java
-GravitinoClient client = ...
-
-// Create a custom policy
 PolicyContent content = PolicyContents.custom(
-    ImmutableMap.of("rule1", 123),
+    ImmutableMap.of("retentionDays", 30),
     ImmutableSet.of(
         MetadataObject.Type.CATALOG,
+        MetadataObject.Type.SCHEMA,
         MetadataObject.Type.TABLE),
-    ImmutableMap.of("key1", "value1"));
+    ImmutableMap.of("owner", "platform"));
+
 Policy policy = client.createPolicy(
-    "my_policy1",
-    "custom",
-    "This is a test policy",
-    true /* enabled */,
-    content);
+    "retention_30d", "custom", "Thirty day retention", true, content);
 ```
 
 </TabItem>
 </Tabs>
 
-### Built-In Iceberg Compaction Policy
+The built-in compaction policy has a fixed content shape, documented in
+[Iceberg compaction policy](./iceberg-compaction-policy.md), and a helper that 
builds it with
+defaults.
 
-For the built-in `system_iceberg_compaction` policy content, field 
definitions, and examples, see [Iceberg compaction 
policy](./iceberg-compaction-policy.md).
+```java
+Policy policy = client.createPolicy(
+    "nightly_compaction",
+    "system_iceberg_compaction",
+    "Compaction defaults",
+    true,
+    PolicyContents.icebergDataCompaction());
+```
 
 ### List Policies
 
-List all the policy names as well as policy objects in a metalake in Gravitino.
+Listing returns names, or full policy objects when `details=true` is set.
 
 <Tabs groupId='language' queryString>
-<TabItem value="shell" label="Shell">
+<TabItem value="shell" label="REST">
 
 ```shell
-# List policy names
 curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
-http://localhost:8090/api/metalakes/test/policies
+  http://localhost:8090/api/metalakes/test/policies
 
-# List policy details
 curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
-http://localhost:8090/api/metalakes/test/policies?details=true
+  "http://localhost:8090/api/metalakes/test/policies?details=true";
 ```
 
 </TabItem>
 <TabItem value="java" label="Java">
 
 ```java
-GravitinoClient client = ...
 String[] policyNames = client.listPolicies();
-
 Policy[] policies = client.listPolicyInfos();
 ```
 
 </TabItem>
 </Tabs>
 
-### Get a Policy by Name
-
-Get a policy by its name.
+### Get a Policy
 
 <Tabs groupId='language' queryString>
-<TabItem value="shell" label="Shell">
+<TabItem value="shell" label="REST">
 
 ```shell
 curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
-http://localhost:8090/api/metalakes/test/policies/my_policy1
+  http://localhost:8090/api/metalakes/test/policies/retention_30d
 ```
 
 </TabItem>
 <TabItem value="java" label="Java">
 
 ```java
-GravitinoClient client = ...
-Policy policy = client.getPolicy("my_policy1");
+Policy policy = client.getPolicy("retention_30d");
 ```
 
 </TabItem>
 </Tabs>
 
-### Update a Policy
+### Alter a Policy
+
+Changes are applied as a list in one request.
 
-Gravitino allows you to update a policy by providing changes.
+| Change             | JSON                                                    
             | Java                                               |
+|--------------------|----------------------------------------------------------------------|----------------------------------------------------|
+| Rename             | `{"@type":"rename","newName":"policy_renamed"}`         
             | `PolicyChange.rename("policy_renamed")`            |
+| Update the comment | `{"@type":"updateComment","newComment":"new_comment"}`  
             | `PolicyChange.updateComment("new_comment")`        |
+| Update the content | 
`{"@type":"updateContent","policyType":"custom","newContent":{...}}` | 
`PolicyChange.updateContent("custom", newContent)` |
 
 <Tabs groupId='language' queryString>
-<TabItem value="shell" label="Shell">
+<TabItem value="shell" label="REST">
 
 ```shell
 curl -X PUT -H "Accept: application/vnd.gravitino.v1+json" \
--H "Content-Type: application/json" -d '{
+  -H "Content-Type: application/json" -d '{
   "updates": [
-    {
-      "@type": "rename",
-      "newName": "my_policy_new"
-    },
-    {
-      "@type": "updateComment",
-      "newComment": "This is my new policy comment"
-    },
     {
       "@type": "updateContent",
       "policyType": "custom",
       "newContent": {
-        "customRules": {
-          "rule1": 456
-        },
-        "supportedObjectTypes": [
-          "CATALOG",
-          "TABLE"
-        ],
-        "properties": {
-          "key1": "new_value1",
-          "key2": "new_value2"
-        }
+        "customRules": {"retentionDays": 90},
+        "supportedObjectTypes": ["CATALOG", "SCHEMA", "TABLE"],
+        "properties": {"owner": "platform"}
       }
     }
   ]
-}' http://localhost:8090/api/metalakes/test/policies/my_policy1
+}' http://localhost:8090/api/metalakes/test/policies/retention_30d
 ```
 
 </TabItem>
 <TabItem value="java" label="Java">
 
 ```java
-GravitinoClient client = ...
 PolicyContent newContent = PolicyContents.custom(
-    ImmutableMap.of("rule1", 456),
+    ImmutableMap.of("retentionDays", 90),
     ImmutableSet.of(
         MetadataObject.Type.CATALOG,
+        MetadataObject.Type.SCHEMA,
         MetadataObject.Type.TABLE),
-    ImmutableMap.of("key1", "new_value1", "key2", "new_value2"));
+    ImmutableMap.of("owner", "platform"));
 
 Policy policy = client.alterPolicy(
-    "my_policy1",
-    PolicyChange.rename("my_policy_new"),
-    PolicyChange.updateComment("This is my new policy comment"),
-    PolicyChange.updateContent("custom", newContent));
+    "retention_30d", PolicyChange.updateContent("custom", newContent));
 ```
 
 </TabItem>
 </Tabs>
 
-Gravitino supports the following policy changes:
-
-| Supported modification | JSON                                                
                 | Java                                                  |
-|------------------------|----------------------------------------------------------------------|-------------------------------------------------------|
-| Rename a policy        | `{"@type":"rename","newName":"policy_renamed"}`     
                 | `PolicyChange.rename("policy_renamed")`               |
-| Update a comment       | 
`{"@type":"updateComment","newComment":"new_comment"}`               | 
`PolicyChange.updateComment("new_comment")`           |
-| Update policy content  | 
`{"@type":"updateContent","policyType":"custom","newContent":{...}}` | 
`PolicyChange.updateContent("test_type", newContent)` |
-
 ### Enable or Disable a Policy
 
-Enable or disable a policy.
-
-The `enabled` field of a policy is only a display attribute that marks whether 
the policy is enabled or disabled.
-It does not affect the actual behavior or characteristics of the policy 
itself. This field is intended for 
-external presentation and does not control policy application logic in 
Gravitino.
-
-The `enabled` field can be used for various purposes, such as:
-- You may want to temporarily disable a policy for auditing or review 
purposes, without deleting it or changing its content.
-- Enabling a policy can be used to indicate that it is ready for use or has 
passed necessary approvals.
-- The `enabled` status can be used in UI filtering or reporting to distinguish 
between active and inactive policies. 
-- An external policy enforcement system can use this field to determine 
whether to execute the corresponding policy.
+The flag is a marker for readers. Gravitino does not act on it, and disabling 
a policy neither
+detaches it nor changes what a consumer receives.
 
 <Tabs groupId='language' queryString>
-<TabItem value="shell" label="Shell">
+<TabItem value="shell" label="REST">
 
 ```shell
-# Disable a policy
-curl -X PATCH -H "Accept: application/vnd.gravitino.v1+json" \
--H "Content-Type: application/json" -d '{
-  "enable": false
-}' http://localhost:8090/api/metalakes/test/policies/my_policy_new
-
-# Enable a policy
 curl -X PATCH -H "Accept: application/vnd.gravitino.v1+json" \
--H "Content-Type: application/json" -d '{
-  "enable": true
-}' http://localhost:8090/api/metalakes/test/policies/my_policy_new
+  -H "Content-Type: application/json" -d '{"enable": false}' \
+  http://localhost:8090/api/metalakes/test/policies/retention_30d
 ```
 
 </TabItem>
 <TabItem value="java" label="Java">
 
 ```java
-GravitinoClient client = ...
-// Disable a policy
-client.disablePolicy("my_policy_new");
-
-// Enable a policy
-client.enablePolicy("my_policy_new");
+client.disablePolicy("retention_30d");
+client.enablePolicy("retention_30d");
 ```
 
 </TabItem>
@@ -282,190 +195,123 @@ client.enablePolicy("my_policy_new");
 
 ### Delete a Policy
 
-Delete a policy by its name.
+Deleting a policy also removes it from every object it was attached to.
 
 <Tabs groupId='language' queryString>
-<TabItem value="shell" label="Shell">
+<TabItem value="shell" label="REST">
 
 ```shell
 curl -X DELETE -H "Accept: application/vnd.gravitino.v1+json" \
-http://localhost:8090/api/metalakes/test/policies/my_policy_new
+  http://localhost:8090/api/metalakes/test/policies/retention_30d
 ```
 
 </TabItem>
 <TabItem value="java" label="Java">
 
 ```java
-GravitinoClient client = ...
-client.deletePolicy("my_policy_new");
+client.deletePolicy("retention_30d");
 ```
 
 </TabItem>
 </Tabs>
 
-## Policy Associations
-
-Gravitino lets you associate and disassociate policies with metadata objects. 
The `CATALOG`, `SCHEMA`, `TABLE`, `FILESET`, `TOPIC`, and `MODEL` object types 
can have policies.
+## Object Operations
 
-### Associate and Disassociate Policies with a Metadata Object
+### Attach and Detach Policies
 
-Associate and disassociate policies with a metadata object by providing the 
object type, object
-name and policy names.
-
-The request path for REST API is 
`/api/metalakes/{metalake}/objects/{metadataObjectType}/{metadataObjectFullName}/policies`.
+Both happen in one request, and either list can be omitted. Catalogs, schemas, 
tables, filesets,
+topics, and models can carry a policy.
 
 <Tabs groupId='language' queryString>
-<TabItem value="shell" label="Shell">
+<TabItem value="shell" label="REST">
 
 ```shell
-# First, create some policies to associate
 curl -X POST -H "Accept: application/vnd.gravitino.v1+json" \
--H "Content-Type: application/json" \
--d '{
-  "name": "policy1", 
-  "policyType": "custom", 
-  "content": {
-    "supportedObjectTypes": ["CATALOG", "TABLE"]
-  }
-}' http://localhost:8090/api/metalakes/test/policies
-
-curl -X POST -H "Accept: application/vnd.gravitino.v1+json" \
--H "Content-Type: application/json" \
--d '{
-  "name": "policy2", 
-  "policyType": "custom", 
-  "content": {
-    "supportedObjectTypes": ["CATALOG", "TABLE"]
-  }
-}' http://localhost:8090/api/metalakes/test/policies
-
-curl -X POST -H "Accept: application/vnd.gravitino.v1+json" \
--H "Content-Type: application/json" \
--d '{
-  "name": "policy3", 
-  "policyType": "custom", 
-  "content": {
-    "supportedObjectTypes": ["CATALOG", "TABLE"]
-  }
-}' http://localhost:8090/api/metalakes/test/policies
-
-# Associate and disassociate policies with a catalog
-curl -X POST -H "Accept: application/vnd.gravitino.v1+json" \
--H "Content-Type: application/json" -d '{
-  "policiesToAdd": ["policy1", "policy2"],
-  "policiesToRemove": ["policy3"]
-}' http://localhost:8090/api/metalakes/test/objects/catalog/my_catalog/policies
-
-# Associate policies with a schema
-curl -X POST -H "Accept: application/vnd.gravitino.v1+json" \
--H "Content-Type: application/json" -d '{
-  "policiesToAdd": ["policy1"]
-}' 
http://localhost:8090/api/metalakes/test/objects/schema/my_catalog.my_schema/policies
+  -H "Content-Type: application/json" -d '{
+  "policiesToAdd": ["retention_30d"],
+  "policiesToRemove": ["retention_7d"]
+}' http://localhost:8090/api/metalakes/test/objects/catalog/catalog1/policies
 ```
 
 </TabItem>
 <TabItem value="java" label="Java">
 
 ```java
-// Assume catalog 'my_catalog' and schema 'my_catalog.my_schema' exist
-Catalog catalog = client.loadCatalog("my_catalog");
+Catalog catalog = client.loadCatalog("catalog1");
 catalog.supportsPolicies().associatePolicies(
-    new String[] {"policy1", "policy2"},
-    new String[] {"policy3"});
+    new String[] {"retention_30d"},
+    new String[] {"retention_7d"});
 
-// You need to load the schema from the catalog
-Schema schema = catalog.asSchemas().loadSchema("my_schema");
-schema.supportsPolicies().associatePolicies(new String[] {"policy1"}, null);
+Schema schema = catalog.asSchemas().loadSchema("schema1");
+schema.supportsPolicies().associatePolicies(new String[] {"retention_30d"}, 
null);
 ```
 
 </TabItem>
 </Tabs>
 
-### List Associated Policies for a Metadata Object
+### List Policies on an Object
 
-List all the policies associated with a metadata object. If a policy is 
inheritable, 
-listing policies of a metadata object will also list the policies of its 
parent metadata objects,
-including the intermediate parent schemas of a multi-level (hierarchical) 
schema.
-
-The request path for REST API is 
`/api/metalakes/{metalake}/objects/{metadataObjectType}/{metadataObjectFullName}/policies`.
+The response includes policies inherited from ancestors. With `details=true` 
each policy carries an
+`inherited` field, which a plain name listing does not.
 
 <Tabs groupId='language' queryString>
-<TabItem value="shell" label="Shell">
+<TabItem value="shell" label="REST">
 
 ```shell
-# List policy names for a catalog
-curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
-http://localhost:8090/api/metalakes/test/objects/catalog/my_catalog/policies
-
-# List policy details for a schema
 curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
-http://localhost:8090/api/metalakes/test/objects/schema/my_catalog.my_schema/policies?details=true
+  
"http://localhost:8090/api/metalakes/test/objects/catalog/catalog1/policies?details=true";
 ```
 
 </TabItem>
 <TabItem value="java" label="Java">
 
 ```java
-Catalog catalog = client.loadCatalog("my_catalog");
+Catalog catalog = client.loadCatalog("catalog1");
 String[] policyNames = catalog.supportsPolicies().listPolicies();
 Policy[] policies = catalog.supportsPolicies().listPolicyInfos();
-
-Schema schema = catalog.asSchemas().loadSchema("my_schema");
-String[] schemaPolicyNames = schema.supportsPolicies().listPolicies();
-Policy[] schemaPolicies = schema.supportsPolicies().listPolicyInfos();
 ```
 
 </TabItem>
 </Tabs>
 
-### Get an Associated Policy by Name for a Metadata Object
-
-Get an associated policy by its name for a metadata object.
-
-The request path for REST API is 
`/api/metalakes/{metalake}/objects/{metadataObjectType}/{metadataObjectFullName}/policies/{policy}`.
+### Get One Policy on an Object
 
 <Tabs groupId='language' queryString>
-<TabItem value="shell" label="Shell">
+<TabItem value="shell" label="REST">
 
 ```shell
 curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
-http://localhost:8090/api/metalakes/test/objects/catalog/my_catalog/policies/policy1
-
-curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
-http://localhost:8090/api/metalakes/test/objects/schema/my_catalog.my_schema/policies/policy1
+  
http://localhost:8090/api/metalakes/test/objects/catalog/catalog1/policies/retention_30d
 ```
 
 </TabItem>
 <TabItem value="java" label="Java">
 
 ```java
-Catalog catalog = client.loadCatalog("my_catalog");
-Policy policy = catalog.supportsPolicies().getPolicy("policy1");
-
-Schema schema = catalog.asSchemas().loadSchema("my_schema");
-Policy schemaPolicy = schema.supportsPolicies().getPolicy("policy1");
+Policy policy = catalog.supportsPolicies().getPolicy("retention_30d");
 ```
 
 </TabItem>
 </Tabs>
 
-### List Metadata Objects Associated with a Policy
+### List Objects Carrying a Policy
 
-List all the metadata objects **directly associated with** a policy.
+The response lists direct attachments only, so a policy attached to a catalog 
returns that catalog
+rather than the objects beneath it.
 
 <Tabs groupId='language' queryString>
-<TabItem value="shell" label="Shell">
+<TabItem value="shell" label="REST">
 
 ```shell
 curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
-http://localhost:8090/api/metalakes/test/policies/policy1/objects
+  http://localhost:8090/api/metalakes/test/policies/retention_30d/objects
 ```
 
 </TabItem>
 <TabItem value="java" label="Java">
 
 ```java
-Policy policy = client.getPolicy("policy1");
+Policy policy = client.getPolicy("retention_30d");
 MetadataObject[] objects = policy.associatedObjects().objects();
 int count = policy.associatedObjects().count();
 ```
diff --git a/docs/manage-tags-in-gravitino.md b/docs/manage-tags-in-gravitino.md
index 70b5d2b76d..016709b7ac 100644
--- a/docs/manage-tags-in-gravitino.md
+++ b/docs/manage-tags-in-gravitino.md
@@ -1,7 +1,6 @@
 ---
 title: "Manage Tags"
 slug: "/manage-tags-in-gravitino"
-date: 2024-07-24
 keyword: "tag management, tag, tags, Gravitino"
 license: "This software is licensed under the Apache License version 2."
 ---
@@ -11,54 +10,24 @@ import TabItem from '@theme/TabItem';
 
 ## Introduction
 
-Gravitino provides a tag system that allows you to manage tags for
-metadata objects. Tags are a way to categorize and organize metadata objects 
in Gravitino.
-
-This document briefly introduces how to use tags in Gravitino by both 
Gravitino Java client and
-REST APIs. If you want to know more about the tag system in Gravitino, refer 
to the
-Javadoc and REST API documentation.
-
-Note that current tag system is a basic implementation, some advanced features 
will be added in
-the future versions.
-
-:::info
-1. Metadata objects are objects that are managed in Gravitino, such as 
`CATALOG`, `SCHEMA`, `TABLE`,
-   `COLUMN`, `FILESET`, `TOPIC`, `COLUMN`, `MODEL`, etc. A metadata object is 
combined by a `type` and a
-   dot-separated `name`. For example, a `CATALOG` object has a name "catalog1" 
with type
-   "CATALOG", a `SCHEMA` object has a name "catalog1.schema1" with type 
"SCHEMA", a `TABLE`
-   object has a name "catalog1.schema1.table1" with type "TABLE", a `COLUMN` 
object has a name 
-   "catalog1.schema1.table1.column1" with type "COLUMN".
-2`CATALOG`, `SCHEMA`, `TABLE`, `FILESET`, `TOPIC`, `MODEL`, and `COLUMN` 
objects can be tagged.
-3. Tags in Gravitino is inheritable, so listing tags of a metadata object will 
also list the
-   tags of its parent metadata objects. For example, listing tags of a `Table` 
will also list
-   the tags of its parent `Schema` and `Catalog`. For catalogs that support 
multi-level
-   (hierarchical) schemas, such as a schema named `a:b:c` (using the 
configured schema
-   separator), the intermediate parent schemas `a:b` and `a` are also part of 
the hierarchy, so
-   their tags are inherited as well.
-4. The same tag can be associated with both parent and child metadata objects. 
But when you list the
-   associated tags of a child metadata object, this tag will be included only 
once in the result
-   list with `inherited` value `false`.
-:::
+This page covers the Gravitino API for tags. For what a tag is, which object 
types can carry one, how inheritance
+resolves, and how to work with tags in the UI, see [Tags](./tags.md).
 
 ## Tag Operations
 
-### Create New Tags
+### Create a Tag
 
-The first step to manage tags is to create new tags. Create a tag by providing 
a
-name, optional comment, and properties.
+A tag needs a name, and can carry a comment and properties.
 
 <Tabs groupId='language' queryString>
-<TabItem value="shell" label="Shell">
+<TabItem value="shell" label="REST">
 
 ```shell
 curl -X POST -H "Accept: application/vnd.gravitino.v1+json" \
--H "Content-Type: application/json" -d '{
-  "name": "tag1",
-  "comment": "This is a tag",
-  "properties": {
-    "key1": "value1",
-    "key2": "value2"
-  }
+  -H "Content-Type: application/json" -d '{
+  "name": "pii",
+  "comment": "Personally identifiable information",
+  "properties": {"owner": "data-governance"}
 }' http://localhost:8090/api/metalakes/test/tags
 ```
 
@@ -67,8 +36,20 @@ curl -X POST -H "Accept: application/vnd.gravitino.v1+json" \
 
 ```java
 GravitinoClient client = ...
-Tag tag =
-    client.createTag("tag1", "This is a tag", ImmutableMap.of("key1", 
"value1", "key2", "value2"));
+Tag tag = client.createTag(
+    "pii",
+    "Personally identifiable information",
+    ImmutableMap.of("owner", "data-governance"));
+```
+
+</TabItem>
+<TabItem value="python" label="Python">
+
+```python
+tag = client.create_tag(
+    tag_name="pii",
+    comment="Personally identifiable information",
+    properties={"owner": "data-governance"})
 ```
 
 </TabItem>
@@ -76,267 +57,271 @@ Tag tag =
 
 ### List Tags
 
-List all the tag names as well as tag objects in a metalake in Gravitino.
+Listing returns names, or full tag objects when `details=true` is set.
 
 <Tabs groupId='language' queryString>
-<TabItem value="shell" label="Shell">
+<TabItem value="shell" label="REST">
 
 ```shell
 curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
-http://localhost:8090/api/metalakes/test/tags
+  http://localhost:8090/api/metalakes/test/tags
 
 curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
-http://localhost:8090/api/metalakes/test/tags?details=true
+  "http://localhost:8090/api/metalakes/test/tags?details=true";
 ```
 
 </TabItem>
 <TabItem value="java" label="Java">
 
 ```java
-GravitinoClient client = ...
 String[] tagNames = client.listTags();
-
 Tag[] tags = client.listTagsInfo();
 ```
 
 </TabItem>
-</Tabs>
+<TabItem value="python" label="Python">
 
-### Get a Tag by Name
+```python
+tag_names = client.list_tags()
+tags = client.list_tags_info()
+```
+
+</TabItem>
+</Tabs>
 
-Get a tag by its name.
+### Get a Tag
 
 <Tabs groupId='language' queryString>
-<TabItem value="shell" label="Shell">
+<TabItem value="shell" label="REST">
 
 ```shell
 curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
-http://localhost:8090/api/metalakes/test/tags/tag1
+  http://localhost:8090/api/metalakes/test/tags/pii
 ```
 
 </TabItem>
 <TabItem value="java" label="Java">
 
 ```java
-GravitinoClient client = ...
-Tag tag = client.getTag("tag1");
+Tag tag = client.getTag("pii");
+```
+
+</TabItem>
+<TabItem value="python" label="Python">
+
+```python
+tag = client.get_tag("pii")
 ```
 
 </TabItem>
 </Tabs>
 
-### Update a Tag
+### Alter a Tag
 
-Gravitino allows you to update a tag by providing a new tag name, comment and 
properties.
+Changes are applied as a list in one request.
+
+| Change             | JSON                                                    
     | Java                                      | Python                       
                |
+|--------------------|--------------------------------------------------------------|-------------------------------------------|----------------------------------------------|
+| Rename             | `{"@type":"rename","newName":"tag_renamed"}`            
     | `TagChange.rename("tag_renamed")`         | 
`TagChange.rename("tag_renamed")`            |
+| Update the comment | `{"@type":"updateComment","newComment":"new_comment"}`  
     | `TagChange.updateComment("new_comment")`  | 
`TagChange.update_comment("new_comment")`    |
+| Set a property     | 
`{"@type":"setProperty","property":"key1","value":"value1"}` | 
`TagChange.setProperty("key1", "value1")` | `TagChange.set_property("key1", 
"value1")`   |
+| Remove a property  | `{"@type":"removeProperty","property":"key1"}`          
     | `TagChange.removeProperty("key1")`        | 
`TagChange.remove_property("key1")`          |
 
 <Tabs groupId='language' queryString>
-<TabItem value="shell" label="Shell">
+<TabItem value="shell" label="REST">
 
 ```shell
 curl -X PUT -H "Accept: application/vnd.gravitino.v1+json" \
--H "Content-Type: application/json" -d '{
+  -H "Content-Type: application/json" -d '{
   "updates": [
-    {
-      "@type": "rename",
-      "newName": "tag2"
-    },
-    {
-      "@type": "updateComment",
-      "newComment": "This is an updated tag"
-    },
-    {
-      "@type": "setProperty",
-      "property": "key3",
-      "value": "value3"
-    },
-    {
-      "@type": "removeProperty",
-      "property": "key1"
-    }
+    {"@type": "updateComment", "newComment": "Reviewed quarterly"},
+    {"@type": "setProperty", "property": "owner", "value": "privacy-office"}
   ]
-}' http://localhost:8090/api/metalakes/test/tags/tag1
+}' http://localhost:8090/api/metalakes/test/tags/pii
 ```
 
 </TabItem>
 <TabItem value="java" label="Java">
 
 ```java
-GravitinoClient client = ...
 Tag tag = client.alterTag(
-    "tag1",
-    TagChange.rename("tag2"),
-    TagChange.updateComment("This is an updated tag"),
-    TagChange.setProperty("key3", "value3"),
-    TagChange.removeProperty("key1"));
+    "pii",
+    TagChange.updateComment("Reviewed quarterly"),
+    TagChange.setProperty("owner", "privacy-office"));
 ```
 
 </TabItem>
-</Tabs>
+<TabItem value="python" label="Python">
 
-Gravitino supports the following tag changes:
+```python
+tag = client.alter_tag(
+    "pii",
+    TagChange.update_comment("Reviewed quarterly"),
+    TagChange.set_property("owner", "privacy-office"))
+```
 
-| Supported modification | JSON                                                
         | Java                                      |
-|------------------------|--------------------------------------------------------------|-------------------------------------------|
-| Rename a tag           | `{"@type":"rename","newName":"tag_renamed"}`        
         | `TagChange.rename("tag_renamed")`         |
-| Update a comment       | 
`{"@type":"updateComment","newComment":"new_comment"}`       | 
`TagChange.updateComment("new_comment")`  |
-| Set a tag property     | 
`{"@type":"setProperty","property":"key1","value":"value1"}` | 
`TagChange.setProperty("key1", "value1")` |
-| Remove a tag property  | `{"@type":"removeProperty","property":"key1"}`      
         | `TagChange.removeProperty("key1")`        |
+</TabItem>
+</Tabs>
 
 ### Delete a Tag
 
-Delete a tag by its name.
+Deleting a tag also removes it from every object it was attached to.
 
 <Tabs groupId='language' queryString>
-<TabItem value="shell" label="Shell">
+<TabItem value="shell" label="REST">
 
 ```shell
 curl -X DELETE -H "Accept: application/vnd.gravitino.v1+json" \
-http://localhost:8090/api/metalakes/test/tags/tag2
+  http://localhost:8090/api/metalakes/test/tags/pii
 ```
 
 </TabItem>
 <TabItem value="java" label="Java">
 
 ```java
-GravitinoClient client = ...
-client.deleteTag("tag2");
+client.deleteTag("pii");
 ```
 
 </TabItem>
-</Tabs>
+<TabItem value="python" label="Python">
 
-## Tag Associations
+```python
+client.delete_tag("pii")
+```
 
-Gravitino lets you associate and disassociate tags with metadata objects. The 
`CATALOG`, `SCHEMA`, `TABLE`, `FILESET`, `TOPIC`, `MODEL`, and `COLUMN` object 
types can be tagged.
+</TabItem>
+</Tabs>
 
-### Associate and Disassociate Tags with a Metadata Object
+## Object Operations
 
-Associate and disassociate tags with a metadata object by providing the object 
type, object
-name and tag names.
+### Attach and Detach Tags
 
-The request path for REST API is 
`/api/metalakes/{metalake}/objects/{metadataObjectType}/{metadataObjectName}/tags`.
+Both happen in one request, and either list can be omitted. The object type 
and full name go in the
+path, so the same call covers catalogs, schemas, tables, views, columns, 
filesets, topics, models,
+and functions.
 
 <Tabs groupId='language' queryString>
-<TabItem value="shell" label="Shell">
+<TabItem value="shell" label="REST">
 
 ```shell
 curl -X POST -H "Accept: application/vnd.gravitino.v1+json" \
--H "Content-Type: application/json" -d '{
-  "tagsToAdd": ["tag1", "tag2"],
-  "tagsToRemove": ["tag3"]
-}' http://localhost:8090/api/metalakes/test/objects/catalog/catalog1/tags
+  -H "Content-Type: application/json" -d '{
+  "tagsToAdd": ["pii"],
+  "tagsToRemove": ["unreviewed"]
+}' 
http://localhost:8090/api/metalakes/test/objects/table/catalog1.schema1.customers/tags
 
 curl -X POST -H "Accept: application/vnd.gravitino.v1+json" \
--H "Content-Type: application/json" -d '{
-  "tagsToAdd": ["tag1"]
-}' 
http://localhost:8090/api/metalakes/test/objects/schema/catalog1.schema1/tags
+  -H "Content-Type: application/json" -d '{
+  "tagsToAdd": ["pii"]
+}' 
http://localhost:8090/api/metalakes/test/objects/fileset/catalog1.schema1.raw_events/tags
 ```
 
 </TabItem>
 <TabItem value="java" label="Java">
 
 ```java
-Catalog catalog1 = ...
-catalog1.supportsTags().associateTags(
-    new String[] {"tag1", "tag2"},
-    new String[] {"tag3"});
+Table customers = ...
+customers.supportsTags().associateTags(
+    new String[] {"pii"},
+    new String[] {"unreviewed"});
 
-Schema schema1 = ...
-schema1.supportsTags().associateTags(new String[] {"tag1"}, null);
+Fileset rawEvents = ...
+rawEvents.supportsTags().associateTags(new String[] {"pii"}, null);
 ```
 
 </TabItem>
-</Tabs>
+<TabItem value="python" label="Python">
 
-### List Associated Tags for a Metadata Object
+```python
+customers = ...
+customers.supports_tags().associate_tags(["pii"], ["unreviewed"])
 
-List all the tags associated with a metadata object. The tags in Gravitino are
-inheritable, so listing tags of a metadata object will also list the tags of 
its parent metadata
-objects, including the intermediate parent schemas of a multi-level 
(hierarchical) schema.
-
-The request path for REST API is 
`/api/metalakes/{metalake}/objects/{metadataObjectType}/{metadataObjectName}/tags`.
+raw_events = ...
+raw_events.supports_tags().associate_tags(["pii"], None)
+```
 
-<Tabs groupId='language' queryString>
-<TabItem value="shell" label="Shell">
+</TabItem>
+</Tabs>
 
-```shell
-curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
-http://localhost:8090/api/metalakes/test/objects/catalog/catalog1/tags
+### List Tags on an Object
 
-curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
-http://localhost:8090/api/metalakes/test/objects/schema/catalog1.schema1/tags
+The response includes tags inherited from ancestors. With `details=true` each 
tag carries an
+`inherited` field, which a plain name listing does not.
 
-curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
-http://localhost:8090/api/metalakes/test/objects/catalog/catalog1/tags?details=true
+<Tabs groupId='language' queryString>
+<TabItem value="shell" label="REST">
 
+```shell
 curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
-http://localhost:8090/api/metalakes/test/objects/schema/catalog1.schema1/tags?details=true
+  
"http://localhost:8090/api/metalakes/test/objects/table/catalog1.schema1.customers/tags?details=true";
 ```
 
 </TabItem>
 <TabItem value="java" label="Java">
 
 ```java
-Catalog catalog1 = ...
-String[] tags = catalog1.supportsTags().listTags();
-Tag[] tagsInfo = catalog1.supportsTags().listTagsInfo();
-
-Schema schema1 = ...
-String[] tags = schema1.supportsTags().listTags();
-Tag[] tagsInfo = schema1.supportsTags().listTagsInfo();
+Table customers = ...
+String[] tagNames = customers.supportsTags().listTags();
+Tag[] tags = customers.supportsTags().listTagsInfo();
 ```
 
 </TabItem>
-</Tabs>
+<TabItem value="python" label="Python">
 
-### Get an Associated Tag by Name for a Metadata Object
+```python
+customers = ...
+tag_names = customers.supports_tags().list_tags()
+tags = customers.supports_tags().list_tags_info()
+```
 
-Get an associated tag by its name for a metadata object.
+</TabItem>
+</Tabs>
 
-The request path for REST API is 
`/api/metalakes/{metalake}/objects/{metadataObjectType}/{metadataObjectName}/tags/{tagName}`.
+### Get One Tag on an Object
 
 <Tabs groupId='language' queryString>
-<TabItem value="shell" label="Shell">
+<TabItem value="shell" label="REST">
 
 ```shell
 curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
-http://localhost:8090/api/metalakes/test/objects/catalog/catalog1/tags/tag1
-
-curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
-http://localhost:8090/api/metalakes/test/objects/schema/catalog1.schema1/tags/tag1
+  
http://localhost:8090/api/metalakes/test/objects/table/catalog1.schema1.customers/tags/pii
 ```
 
 </TabItem>
 <TabItem value="java" label="Java">
 
 ```java
-Catalog catalog1 = ...
-Tag tag = catalog1.supportsTags().getTag("tag1");
+Tag tag = customers.supportsTags().getTag("pii");
+```
+
+</TabItem>
+<TabItem value="python" label="Python">
 
-Schema schema1 = ...
-Tag tag = schema1.supportsTags().getTag("tag1");
+```python
+tag = customers.supports_tags().get_tag("pii")
 ```
 
 </TabItem>
 </Tabs>
 
-### List Metadata Objects Associated with a Tag
+### List Objects Carrying a Tag
 
-List all the metadata objects associated with a tag.
+The response lists direct attachments only, so a tag attached to a catalog 
returns that catalog
+rather than the objects beneath it.
 
 <Tabs groupId='language' queryString>
-<TabItem value="shell" label="Shell">
+<TabItem value="shell" label="REST">
 
 ```shell
 curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
-http://localhost:8090/api/metalakes/test/tags/tag1/objects
+  http://localhost:8090/api/metalakes/test/tags/pii/objects
 ```
 
 </TabItem>
 <TabItem value="java" label="Java">
 
 ```java
-Tag tag = ...
+Tag tag = client.getTag("pii");
 MetadataObject[] objects = tag.associatedObjects().objects();
 int count = tag.associatedObjects().count();
 ```
diff --git a/docs/policies.md b/docs/policies.md
new file mode 100644
index 0000000000..828c436831
--- /dev/null
+++ b/docs/policies.md
@@ -0,0 +1,187 @@
+---
+title: "Policies"
+slug: "/policies"
+keyword: "policy, policies, governance, metadata object, Gravitino"
+license: "This software is licensed under the Apache License version 2."
+---
+
+## Introduction
+
+A policy is a named set of rules that you create once in a metalake and attach 
to metadata objects.
+Attaching a policy to a catalog or schema applies it to everything beneath, so 
a setting that varies
+by table can be expressed once at the level where it holds and overridden 
where it does not.
+
+Tags and policies are close cousins, and the difference is what they carry. A 
tag classifies, and
+its content is its name. A policy prescribes, and its content is a set of 
rules something acts on.
+
+Policies come in two kinds. A built-in policy has a type that Gravitino 
defines and a consumer that
+acts on it. A custom policy carries rules of your own, which Gravitino stores, 
inherits, and serves
+back to whatever system you build around it.
+
+Common uses:
+
+- Setting table maintenance behavior for a whole catalog rather than table by 
table, and letting new
+  tables pick it up without further work
+- Recording a rule once against metadata that lives in several catalogs, so 
every engine reaching
+  those objects through Gravitino sees the same rule
+- Feeding an external enforcement or scheduling system that reads policies 
from Gravitino rather
+  than keeping its own copy of what applies where
+
+## Quick Start
+
+**1. Create the policy.** Policies are created from the policy list in the UI, 
which creates custom
+policies. A policy needs a name, the object types it supports, and its rules. 
Built-in policies are
+created over REST.
+
+**2. Attach it to an object.** Open the catalog, schema, table, fileset, 
topic, or model you want to
+govern and add the policy from its policy control. Only policies that already 
exist in the metalake
+are offered.
+
+**3. See where the policy is attached.** Selecting a policy name in the policy 
list shows the
+objects it is attached to directly.
+
+## The Policy Model
+
+### Policy Types
+
+| Type                        | Rules                                | 
Consumed by               |
+|-----------------------------|--------------------------------------|---------------------------|
+| `system_iceberg_compaction` | Compaction thresholds and scheduling | Table 
maintenance service |
+| `custom`                    | A free-form map you define           | A 
system you provide      |
+
+A built-in type has a name beginning with `system_` and a content shape 
Gravitino defines. The
+compaction policy is documented in [Iceberg compaction 
policy](./iceberg-compaction-policy.md), and
+the service that acts on it in
+[Table maintenance service](./table-maintenance-service/optimizer.md).
+
+A custom policy has type `custom`, and Gravitino makes no attempt to interpret 
what is inside
+`customRules`. The rules are stored, inherited down the hierarchy, and 
returned to any client that
+asks.
+
+The UI creates custom policies only. A built-in policy is created over REST 
with its own content
+shape.
+
+### What Can Carry a Policy
+
+A metadata object is identified by a type and a name, with each level below 
the catalog separated by
+a dot. Six object types can carry a policy.
+
+| Object type | Name form                                     |
+|-------------|-----------------------------------------------|
+| `CATALOG`   | `{catalog_name}`                              |
+| `SCHEMA`    | `{catalog_name}.{schema_name}`                |
+| `TABLE`     | `{catalog_name}.{schema_name}.{table_name}`   |
+| `FILESET`   | `{catalog_name}.{schema_name}.{fileset_name}` |
+| `TOPIC`     | `{catalog_name}.{schema_name}.{topic_name}`   |
+| `MODEL`     | `{catalog_name}.{schema_name}.{model_name}`   |
+
+Columns, views, and functions cannot carry a policy, which is narrower than
+[tags](./tags.md). A metalake cannot carry one either, so to reach every object
+in a catalog, attach the policy to the catalog.
+
+Each policy also declares its own `supportedObjectTypes`, which narrows the 
list further for that
+policy.
+
+### Content
+
+Policy content has three parts: the `supportedObjectTypes` list, the rules, 
and properties.
+
+`supportedObjectTypes` is fixed when the policy is created and cannot be 
changed afterward, so a
+policy meant for tables only stays that way for its lifetime.
+
+The rules are what a consumer evaluates. For a custom policy they live under 
`customRules` as a map
+you define, where the name is yours and the value is any JSON value.
+
+```json
+"customRules": {
+  "retentionDays": 30,
+  "maxTableSizeGb": 500,
+  "requiresApproval": true
+}
+```
+
+Gravitino does not interpret those names or values. Whatever consumes the 
policy decides what
+`retentionDays` means and what to do about it.
+
+A built-in policy has a rule set Gravitino defines, and the service that 
consumes it documents how
+those rules are applied. The compaction policy carries `minDataFileMse`, 
`minDeleteFileNumber`,
+`dataFileMseWeight`, `deleteFileNumberWeight`, `max-partition-num`, and a 
trigger and score
+expression, plus any `job.options.` entries passed through to the job. Those 
names and their
+meanings are covered in [Iceberg compaction 
policy](./iceberg-compaction-policy.md).
+
+Properties describe the policy itself rather than the behavior it asks for. 
Rules change as you
+adjust thresholds, and properties stay stable. The compaction policy uses 
properties for its
+strategy type and job template name, which tell the table maintenance service 
what to run, and those
+are set by Gravitino rather than by you. For a custom policy, properties are 
yours, and suit facts
+such as which team owns the policy, which system consumes it, or which version 
of a rule set it
+represents. Anything evaluated against an object belongs in rules instead.
+
+Properties sit on the policy rather than on an attachment, so every object 
carrying the policy sees
+the same values.
+
+### The Enabled Flag
+
+The `enabled` flag marks a policy as active or inactive for readers. Gravitino 
does not act on it,
+so disabling a policy does not detach it or change what a consumer receives. 
Treat it as a signal to
+whoever reads the policy, useful for holding a policy through review without 
deleting it.
+
+### Inheritance
+
+An object shows the policies attached to it plus the policies attached to each 
of its ancestors, so
+a policy on a catalog applies to every schema, table, fileset, topic, and 
model beneath it. For
+catalogs that support multi-level schemas, the intermediate schemas are 
ancestors too.
+
+Each policy appears once, whether it reaches the object through one ancestor 
or several. A policy
+attached directly to the object counts as direct even when an ancestor carries 
it too.
+
+Direct and inherited attachments are distinguishable. In the UI an inherited 
policy is marked with a
+lock icon. Over REST, a policy listing requested with `details=true` carries 
an `inherited` field on
+each policy, which a plain listing of names does not.
+
+A policy that reaches an object only by inheritance cannot be removed there. 
Detach it from the
+ancestor that carries it, which affects every other object beneath that 
ancestor as well.
+
+Inheritance is resolved when the object is read rather than stored on the 
object, so attaching a
+policy to a catalog takes effect immediately for tables created afterward.
+
+## Working With Policies in the UI
+
+### Managing the Policy Set
+
+The policy list holds every policy in the metalake and can be searched. A 
policy can be renamed, its
+comment and rules edited, and its enabled flag switched from there. Policies 
created over REST,
+including built-in ones, appear in the list alongside the rest.
+
+Deleting a policy removes it from every object it was attached to, with no 
warning about how many
+objects that affects and no way to recover the attachments.
+
+### Attaching and Detaching
+
+Policies attach from the object rather than from the policy, so open the 
object and use the policy
+control there. Inherited policies carry no remove control. Detaching removes 
the direct attachment
+only, so an object still shows a policy it inherits from an ancestor.
+
+### Finding Where a Policy Is Used
+
+Selecting a policy name opens a view listing the objects the policy is 
attached to directly.
+Inherited reach is not included, so a policy attached to one catalog lists 
that catalog rather than
+the tables under it.
+
+## Permissions
+
+Policy permissions are held on the policy, and apply in addition to 
permissions on the objects being
+governed.
+
+| Privilege       | Grantable on                 | What it allows              
                   |
+|-----------------|------------------------------|------------------------------------------------|
+| `CREATE_POLICY` | Metalake                     | Creating policies in the 
metalake              |
+| `APPLY_POLICY`  | Metalake, or a single policy | Reading a policy and 
attaching or detaching it |
+
+Altering and deleting a policy are reserved for the metalake owner and the 
policy owner. Attaching a
+policy also requires access to the object being governed. Policy listings show 
only the policies
+that user is allowed to read.
+
+## Using the API
+
+Policies can be created, attached, and read over REST and through the Java 
client. Endpoints, payload
+shapes, and worked examples are in [Manage 
Policies](./manage-policies-in-gravitino.md).
diff --git a/docs/tags.md b/docs/tags.md
new file mode 100644
index 0000000000..ce4a334e63
--- /dev/null
+++ b/docs/tags.md
@@ -0,0 +1,152 @@
+---
+title: "Tags"
+slug: "/tags"
+keyword: "tag, tags, labels, classification, metadata object, Gravitino"
+license: "This software is licensed under the Apache License version 2."
+---
+
+## Introduction
+
+A tag is a named label that you create once in a metalake and attach to any 
number of metadata
+objects. A tag carries an optional comment and a set of properties, so it can 
hold a small amount of
+structured detail beyond its name. The same tag can be attached to a catalog, 
a table, and a single
+column at the same time.
+
+Tags travel with the metadata rather than with the data, so a tag applied to a 
table is visible to
+every engine and every client that reaches that table through Gravitino, no 
matter which system
+actually stores it. That makes a tag the practical way to say something once 
about metadata that
+lives in several catalogs at once.
+
+Gravitino stores tags, resolves them down the metadata hierarchy, and shows 
them wherever the object
+appears. Common uses:
+
+- Recording a classification once, on the catalog or schema, and having every 
table and column
+  beneath it carry that classification without further work
+- Answering coverage questions across catalogs you do not own, such as which 
objects anywhere in the
+  metalake are marked as personal data
+- Carrying a classification that arrived from another catalog through to the 
engines and clients that
+  read metadata from Gravitino
+- Marking objects for a downstream consumer to act on, such as a job that 
reads the tags on an object
+  before deciding what to do with it
+
+## Quick Start
+
+**1. Create the tag.** Tags are created from the tag list in the UI. A tag 
needs a name, and can
+also carry a comment and any properties you want to keep with it.
+
+**2. Attach it to an object.** Open the catalog, schema, or table you want to 
label and add the tag
+from its tag control. Only tags that already exist in the metalake are 
offered, so create the tag
+first and attach it second.
+
+**3. Label a single column.** A table's column list carries its own tag 
control on each row, so a
+tag can sit on one column without applying to the rest of the table.
+
+**4. See where the tag is attached.** Selecting a tag name in the tag list 
shows the objects it is
+attached to directly.
+
+## The Tag Model
+
+### What Can Carry a Tag
+
+A metadata object is identified by a type and a name, with each level below 
the catalog separated by
+a dot. Nine object types can carry a tag.
+
+| Object type | Name form                                                 |
+|-------------|-----------------------------------------------------------|
+| `CATALOG`   | `{catalog_name}`                                          |
+| `SCHEMA`    | `{catalog_name}.{schema_name}`                            |
+| `TABLE`     | `{catalog_name}.{schema_name}.{table_name}`               |
+| `VIEW`      | `{catalog_name}.{schema_name}.{view_name}`                |
+| `COLUMN`    | `{catalog_name}.{schema_name}.{table_name}.{column_name}` |
+| `FILESET`   | `{catalog_name}.{schema_name}.{fileset_name}`             |
+| `TOPIC`     | `{catalog_name}.{schema_name}.{topic_name}`               |
+| `MODEL`     | `{catalog_name}.{schema_name}.{model_name}`               |
+| `FUNCTION`  | `{catalog_name}.{schema_name}.{function_name}`            |
+
+A metalake cannot carry a tag, so there is no single attachment point that 
covers everything at
+once. To reach every object in a catalog, attach the tag to the catalog.
+
+The UI attaches tags on catalogs, schemas, tables, and columns. For the other 
types, use the
+REST API described at the end of this page.
+
+### Names and Properties
+
+A tag name is unique within its metalake and is the identifier used everywhere 
else, so renaming a
+tag changes what every stored request has to ask for.
+
+A name is up to 64 characters of letters, digits, underscores, slashes, equals 
signs, and hyphens.
+A separator convention such as `pii/email` keeps a growing set readable.
+
+Properties are free-form key and value pairs on the tag itself rather than on 
the attachment, so
+every object carrying the tag sees the same values. Properties suit facts 
about the tag, such as
+which team owns it or which external system it came from. Properties do not 
suit facts about one
+tagged object.
+
+### Inheritance
+
+An object shows the tags attached to it plus the tags attached to each of its 
ancestors, so a tag
+on a catalog appears on every schema, table, and column beneath it. For 
catalogs that support
+multi-level schemas, the intermediate schemas are ancestors too, so a schema 
two levels down
+inherits from both of the schemas above it.
+
+Each tag appears once, whether it reaches the object through one ancestor or 
several. A tag attached
+directly to the object counts as direct even when an ancestor carries it too.
+
+The two are distinguishable. In the UI an inherited tag is marked with a lock 
icon. Over REST, a tag
+listing requested with `details=true` carries an `inherited` field on each 
tag, which a plain listing
+of names does not.
+
+A tag that reaches an object only by inheritance cannot be removed there. 
Detach it from the
+ancestor that carries it, which affects every other object beneath that 
ancestor as well.
+
+Inheritance is resolved when the object is read rather than stored on the 
object, so attaching a
+tag to a catalog takes effect immediately for tables created afterward.
+
+## Working With Tags in the UI
+
+### Managing the Tag Set
+
+The tag list holds every tag in the metalake with its comment and creation 
time, and can be
+searched. A tag can be renamed and its comment and properties edited from 
there.
+
+Deleting a tag removes it from every object it was attached to. There is no 
warning about how many
+objects that affects and no way to recover the attachments, so check where a 
tag is used before
+deleting one.
+
+### Attaching and Detaching
+
+Tags attach from the object rather than from the tag, so open the catalog, 
schema, or table and use
+the tag control there. Inherited tags carry no remove control. Detaching 
removes the direct
+attachment only, so an object still shows a tag it inherits from an ancestor.
+
+Column tags are edited from the column list on the table page. A column shows 
its own tags plus
+everything it inherits from the table, schema, and catalog above it, which is 
usually most of what
+is listed.
+
+### Finding Where a Tag Is Used
+
+Selecting a tag name opens a view listing the objects the tag is attached to 
directly. Inherited
+reach is not included, so a tag attached to one catalog lists that catalog 
rather than the tables
+under it. Coverage questions that span a subtree are answered by walking the 
objects and reading
+the tags on each one.
+
+## Permissions
+
+Tag permissions are held on the tag, and apply in addition to permissions on 
the objects being
+tagged.
+
+| Privilege    | Grantable on              | What it allows                    
          |
+|--------------|---------------------------|---------------------------------------------|
+| `CREATE_TAG` | Metalake                  | Creating tags in the metalake     
          |
+| `APPLY_TAG`  | Metalake, or a single tag | Reading a tag and attaching or 
detaching it |
+
+Altering and deleting a tag are reserved for the metalake owner and the tag 
owner.
+Attaching a tag also requires access to the object being tagged, so a user who 
can apply a tag
+cannot use it to reach an object they could not otherwise see. Tag listings 
show only the tags that
+user is allowed to read.
+
+## Using the API
+
+Tags can be created, attached, and read over REST and through the Java and 
Python clients, which is
+also the only way to tag views, filesets, topics, models, and functions today. 
Endpoints, payload
+shapes, and worked examples are in [Manage 
Tags](./manage-tags-in-gravitino.md).

Reply via email to