From cc52d51fb509a43b114e156a2d7567440a339351 Mon Sep 17 00:00:00 2001 From: 1Pro-L4R8 Date: Tue, 27 Oct 2026 17:01:04 +0700 Subject: [PATCH] docs: catalog guard authorizes renames and commits by their tables (platform #758) Document what a Catalog Client's grants must cover for a rename, a single- and multi-table commit, a recursive namespace drop and remote signing; case-insensitive grant matching; Iceberg JSON refusals and the 413 body caps. Add an upgrade note and KI-311 to KI-314. Co-Authored-By: Claude Opus 5.5 --- .../control-plane/catalogs/catalog-detail.md | 32 +++++++++++++++++++ .../classifications/tagging.md | 2 +- .../deploy-operate/storage-and-catalog.md | 7 ++-- .../troubleshooting/catalog-storage-errors.md | 14 ++++++++ platform/deploy-operate/upgrade.md | 18 +++++++++++ platform/faq.md | 8 +++++ platform/known-issues.md | 6 +++- 7 files changed, 82 insertions(+), 5 deletions(-) diff --git a/platform/control-plane/catalogs/catalog-detail.md b/platform/control-plane/catalogs/catalog-detail.md index e402a38..58f605e 100644 --- a/platform/control-plane/catalogs/catalog-detail.md +++ b/platform/control-plane/catalogs/catalog-detail.md @@ -68,6 +68,36 @@ The table shows each role's **Consumer**, **Grants** (for example `catalog · ma A grant on one table stays on the table's old name when the table is renamed: the role loses access to the renamed table and gains it on a new table created under the old name (KI-309). Add the grant on the new name and remove the old one. +### What each operation needs {#what-each-operation-needs} + +egret checks every Iceberg request a Catalog Client sends against its role's grants before the catalog sees it. A grant on the catalog covers every namespace and table; a grant on a namespace covers the tables and views directly in it, not those in a child namespace (a grant on `corp` does not cover `corp.fin.ledger`); a grant on a table covers that table or view only. **write** and **manage** both permit writes. + +| Operation | What the role needs | +|---|---| +| Load or list a table, view or namespace | Any grant covering it. | +| Create a table or view, or stage a create | **write** or **manage** on the catalog, or on the namespace it is created in. A grant on a table name never creates that table. | +| Commit to one table (`POST …/namespaces/{ns}/tables/{t}`) | **write** or **manage** covering the table the commit changes: the table named by the body's `identifier` when it has one, otherwise the table in the path. A view commit is decided on the view in its path. | +| Commit to several tables (`POST …/transactions/commit`) | **write** or **manage** covering **every** table in the commit. One table that is not covered refuses the whole commit, and nothing is committed. | +| Rename a table or view | On the **source**, what a drop needs: **write** or **manage** covering the source table or view. On the **destination**, what a create needs: **write** or **manage** on the catalog, or on the destination namespace. A grant on the destination table name is not enough. | +| Drop a table or view | **write** or **manage** covering it. | +| Drop a namespace recursively (with everything in it) | **write** or **manage** on the **catalog**. A namespace grant still drops an empty namespace, or the namespace's tables one by one. | +| Remote S3 request signing (`…/aws/s3/sign`) | Not used by egret: refused with 403 whatever the grants. Clients read and write table files with vended credentials. | + +An engine's client holds `catalog · manage`, so it can do all of these. For example, a role with **write** on the namespace `sales` and on the namespace `stage` can rename `sales.orders` to `stage.orders`; a role with **write** on the table `sales.orders` and a grant on the table `stage.orders` cannot, because the destination needs a namespace grant. + +**Names match the way the catalog resolves them.** The catalog treats table and namespace names case-insensitively (and also equates width forms and invisible characters, but not accents), so a grant does too: a grant on `sales.Secret` covers `sales.secret` and `SALES.SECRET`, but not `sales.secrét`. Two grants that differ only in case are the same grant. + +**Refusals.** egret answers in the Iceberg REST error format, `{"error": {"message", "type", "code"}}`, so a client such as PyIceberg raises a typed error (`ForbiddenError` for a 403). A refusal names the side that failed and repeats only the names the client sent, so it does not tell whether a table exists: + +| Code | When | Example message | +|---|---|---| +| 403 | The grant does not cover the operation | *role 'etl' may not rename sales.orders: renaming needs write on the source, as dropping it would* | +| 403 | A rename destination without a namespace grant | *role 'etl' may not rename into stage.orders: renaming needs write on namespace stage, as creating a table there would* | +| 403 | A multi-table commit with an uncovered table | *role 'etl' may not commit to sales.orders: a multi-table commit needs write on every table it changes, as updating it alone would; nothing was committed* | +| 403 | A recursive namespace drop without a catalog grant | *role 'etl' may not drop namespace corp recursively: a recursive drop deletes every child namespace and table, so it needs a catalog-level write or manage grant* | +| 400 | A rename or commit body egret cannot read | *malformed rename request: …*, *malformed multi-table commit: …*, *malformed table commit: …* | +| 413 | A rename body over 1 MiB, or a commit body over 16 MiB | *the body of a rename request is larger than 1048576 bytes* | + ## Catalog Clients (warehouse) *Machine credentials (client id + secret) an engine or notebook uses to authenticate.* @@ -111,6 +141,8 @@ A dropped table keeps its classifications in the **Recycle bin**, and **Undrop** - A catalog test before attaching checks only that the host answers on its port; a TPC-H catalog has no host to check. - Attach and detach on Trino restart the coordinator. +- A Catalog Client with only a **read** grant receives vended S3 credentials that can also write and delete the files of the table it loads, so it can damage that table without any catalog write (KI-311). Give **read** grants only to clients you would trust with write access to those tables until this is fixed. +- Any **write** grant lets a role create and list namespaces, and a grant on one table lets it list every table name in that namespace (KI-312). ## Further reading diff --git a/platform/data-governance/classifications/tagging.md b/platform/data-governance/classifications/tagging.md index 41339ab..137fa58 100644 --- a/platform/data-governance/classifications/tagging.md +++ b/platform/data-governance/classifications/tagging.md @@ -109,7 +109,7 @@ The compile and recycle-bin refusals come at once. The engine refusal comes afte - **Drop a table:** the classifications stay on its name. Where used marks the table **Dropped**, and the Governance overview reports it as information. Undrop it from the catalog's **Recycle bin** and it is classified again. On StarRocks a view over it can show real values until the plugin's next poll (KI-305). - **Drop a table, then create another one under its name:** if you rename the new table away, its classifications move with it and are also kept on the old name, because the dropped table may still be undropped. -- **Drop a namespace with everything in it** (a recursive drop): egret removes every classification under the namespace, on its tables and columns, and the classifications its views carried. Views in other schemas that read its tables lose the classifications they derived from them. **Recent changes** records, for example, *Catalog client engine-docs-trino removed 12 classification assignments with namespace sales (namespace dropped)*. On a catalog with soft delete (one with a **Recycle bin**), the catalog refuses a recursive drop unless it is also forced (*Cannot recursively delete namespace with soft-deletion without force flag*), so the classifications go only with a forced drop, and stay when nothing was dropped. +- **Drop a namespace with everything in it** (a recursive drop): egret removes every classification under the namespace, on its tables and columns, and the classifications its views carried. Views in other schemas that read its tables lose the classifications they derived from them. **Recent changes** records, for example, *Catalog client engine-docs-trino removed 12 classification assignments with namespace sales (namespace dropped)*. On a catalog with soft delete (one with a **Recycle bin**), the catalog refuses a recursive drop unless it is also forced (*Cannot recursively delete namespace with soft-deletion without force flag*), so the classifications go only with a forced drop, and stay when nothing was dropped. Only a Catalog Client with a catalog-level **write** or **manage** grant may drop a namespace recursively, as an engine's client can; see [What each operation needs](../../control-plane/catalogs/catalog-detail.md#what-each-operation-needs). - **Drop an empty namespace:** nothing changes. The catalog refuses to drop a namespace that still holds a table, dropped or not. ### Classifications left on an old name diff --git a/platform/deploy-operate/storage-and-catalog.md b/platform/deploy-operate/storage-and-catalog.md index 4a3775a..1fe7293 100644 --- a/platform/deploy-operate/storage-and-catalog.md +++ b/platform/deploy-operate/storage-and-catalog.md @@ -89,8 +89,8 @@ s3.path-style-access=true ``` With `vended-credentials-enabled`, engines get credentials scoped to the table they load and the static `s3.aws-*` keys are -not needed. A warehouse is provisioned with STS enabled and remote signing off, because the embedded mount exposes no -signer endpoint. +not needed. A warehouse is provisioned with STS enabled and remote signing off, and egret refuses every S3 signer request +(`…/aws/s3/sign`) with 403, whatever the client's grants. ### Setting it up in the app @@ -98,7 +98,8 @@ signer endpoint. 2. **Control Plane → Catalogs → New catalog**: create an *egret warehouse* over that storage connection. This creates the warehouse in the embedded catalog. 3. Open the catalog. On **Roles & grants**, add a role and give it a `write` grant (`write` and `manage` permit writes; any - grant permits reads). + grant permits reads). Creating a table, renaming into a namespace and dropping a namespace recursively need more than a + table grant; see [What each operation needs](../control-plane/catalogs/catalog-detail#what-each-operation-needs). 4. On **Catalog Clients**, add a client bound to that role and copy the client id and secret. The secret is shown once. 5. The **Connect** tab shows the catalog URI, warehouse name and OAuth token URL for this deployment, with ready-made snippets for PyIceberg, Trino and StarRocks. diff --git a/platform/deploy-operate/troubleshooting/catalog-storage-errors.md b/platform/deploy-operate/troubleshooting/catalog-storage-errors.md index 2db4ec0..366d31d 100644 --- a/platform/deploy-operate/troubleshooting/catalog-storage-errors.md +++ b/platform/deploy-operate/troubleshooting/catalog-storage-errors.md @@ -49,9 +49,19 @@ of a real failure: | Symptom | Cause | |---|---| | 403 on a table that exists | The client's role has no grant, or a `read` grant where the call writes. `write` and `manage` permit writes; any grant permits reads. | +| 403 on a rename (*may not rename …* or *may not rename into …*) | A rename needs `write` covering the source table and a `catalog` or `namespace` grant on the destination namespace; a grant on the destination table name is not enough. | +| 403 on a commit (*may not commit to …*) | A commit needs `write` covering the table it changes, the table named in the body's `identifier` when there is one. A multi-table commit needs it on every table, and one uncovered table refuses the whole commit. | +| 403 on a recursive namespace drop | A recursive drop needs a `catalog`-level `write` or `manage` grant. | +| 403 *remote signing is not used by egret* | The client asked the S3 request signer. egret does not sign requests; configure the client for vended credentials. | +| 400 *malformed rename request*, *malformed multi-table commit* or *malformed table commit* | The request body is not the JSON the Iceberg REST API defines. | +| 413 *the body of a … is larger than … bytes* | A rename body is limited to 1 MiB, and a commit body to 16 MiB. | | 403 on the warehouse | The token's warehouse differs from the one requested. | | Warehouse creation refused | A warehouse without a `key_prefix` claims the whole bucket, and the catalog rejects a location that overlaps another warehouse. Give each its own bucket or prefix. | +egret's refusals use the Iceberg REST error format, `{"error": {"message", "type", "code"}}`, so the client shows the +message above in a typed error (PyIceberg raises `ForbiddenError` for a 403). The full list of what each operation needs is +in [What each operation needs](../../control-plane/catalogs/catalog-detail#what-each-operation-needs). + ### The advertised address is wrong ```bash @@ -64,6 +74,10 @@ If `issuer` or `token_endpoint` names an address clients cannot reach (`http://l provisioned**, because their security integration compares the old issuer byte for byte; re-provision them. See [Configuration](../configuration#address-settings-that-people-confuse). +Behind the demo stack's edge on a port other than 80, `GET /catalog/v1/config` advertises its `uri` without the port (for +example `http://egret.localtest.me/catalog` on port 18780). PyIceberg follows that address and fails with *connection +refused* (KI-314). A client that reaches egret directly on the stack's network is not affected. + ## The bucket does not exist Nothing creates the bucket: not Egret, and not the development compose file. The server starts anyway, because it does not diff --git a/platform/deploy-operate/upgrade.md b/platform/deploy-operate/upgrade.md index c3ad4ad..48f5581 100644 --- a/platform/deploy-operate/upgrade.md +++ b/platform/deploy-operate/upgrade.md @@ -147,6 +147,24 @@ check *classifications on tables that do not exist* and *classifications on colu classification to the new name and remove it from the old one. See [Renames and drops](../data-governance/classifications/tagging#renames-and-drops). +### Catalog renames and commits are authorized by their tables (platform #758) + +From platform #758 egret's Iceberg catalog decides a rename, a commit and a recursive namespace drop on the tables they +change. There is no migration and no API change. Engines provisioned by egret hold `catalog · manage` and are not affected; +Trino's creates, writes, renames and drops were checked. A Catalog Client with narrower grants may now be refused calls it +could make before: + +- a rename needs `write` on the source table and a `catalog` or `namespace` grant on the destination namespace (a grant on + the destination table name no longer suffices); +- a multi-table commit needs `write` on every table in it; +- a recursive namespace drop needs a `catalog`-level `write` or `manage` grant; +- remote S3 signing is refused with 403. + +Grants now match names case-insensitively, as the catalog does, so two grants that differ only in case are the same grant. +A role reaches no table it could not already reach by spelling the name as its grant does. Refusals are Iceberg JSON errors, and rename and commit bodies are +limited to 1 MiB and 16 MiB (413). See +[What each operation needs](../control-plane/catalogs/catalog-detail#what-each-operation-needs). + ### StarRocks 4.1.6 enforces view privileges See [Engines and Ranger setup](./engines-and-ranger#supported-versions). diff --git a/platform/faq.md b/platform/faq.md index bda4ef7..5525171 100644 --- a/platform/faq.md +++ b/platform/faq.md @@ -168,6 +168,14 @@ rename until the engines serve them under the new name, which takes up to 90 sec A drop keeps them for an undrop, and a recursive namespace drop removes them. Grants, rules and hand-written policies written on the table's path do not follow (KI-308). See [Renames and drops](./data-governance/classifications/tagging#renames-and-drops). +### Why is a catalog client refused a rename or a commit that its grant seems to allow? + +egret authorizes a rename and a commit by the tables they change. A rename needs `write` on the source table, as a drop +would, and a `catalog` or `namespace` grant on the destination namespace, as a create would; a grant on the destination +table name is not enough. A commit needs `write` on the table its body names, and a multi-table commit on every table in it. +A recursive namespace drop needs a `catalog`-level grant. Grants match names case-insensitively, as the catalog does. See +[What each operation needs](./control-plane/catalogs/catalog-detail#what-each-operation-needs). + ### Why did my StarRocks policies stop applying after a restart? The activation `access_control = ranger` lives in the front end's `fe.conf`, which a recreated container regenerates, and diff --git a/platform/known-issues.md b/platform/known-issues.md index 8ab9d95..78a397f 100644 --- a/platform/known-issues.md +++ b/platform/known-issues.md @@ -37,7 +37,7 @@ and missing pieces. Entries are **recorded, not fixed**; they feed the product r ## Findings from the detailed pages -Collected while writing the detailed pages for each area (October 2026), checked against the running demo instance and the product source. KI-154 to KI-213 come from re-verifying every area on the Docker demo stack (`just demo-up`, October 2026), which also closed or updated older rows (marked *Status* or *Docker demo stack*). KI-214 to KI-235 come from a whole-app UI consistency sweep of 78 screens (admin, light theme, 1440×900, same stack), then the dark theme, 768 and 390 px widths, the editors and a non-admin member. KI-236 to KI-246 come from the Wave 1 fixes of the Known-issues fix programme (October 2026), which also closed the rows marked *fixed in platform #…*. KI-247 to KI-259 come from the Wave 2 fixes (SQL Lab, charts, datasets and the Data API: platform #720, #721, #724 and #725), which closed or partly closed the rows they name. KI-260 to KI-266 come from the Wave 2b fixes (alerts and reports delivery, authentication and roles, engines and monitor: platform #726, #727 and #728), which closed the rows they name; KI-021 was closed as by design and KI-148 as no longer applying. KI-267 to KI-269 come from the Wave 3 page-structure fix (titles, page width, tabs, status badges, list links and detail headers: platform #734), which closed KI-214, KI-215, KI-216, KI-222, KI-223 and KI-224; KI-270 was seen during the docs screenshot re-shoot (docs #31). KI-271 to KI-279 come from the Wave 3 controls and states fixes (shared select and date picker, Notifications and the dashboard Filter settings panel: platform #736; the refusal inside the shell, sidebar and settings cleanup, small-screen layout: platform #737) and from the storage and Docker engine fix (platform #735), which closed KI-005, KI-012, KI-217, KI-218 and KI-235 (the last two partly as written), KI-225, KI-227, KI-230, KI-231, KI-233, KI-234, KI-236 and KI-263. KI-280 to KI-283 come from the Wave 3 dashboard fixes (platform #739), which closed KI-003, KI-064 to KI-069, KI-074, KI-075 and KI-168; the SQL Lab fixes of the same wave (platform #740) closed KI-082 to KI-085, KI-089, KI-091 to KI-094, KI-160 to KI-162, KI-228 and KI-229 and found no new defect. KI-303 to KI-307 come from StarRocks enforcing whole-table classifications (platform #755), which closed KI-286 and KI-288; KI-303 was found while building it and the others in its live check. KI-284 and KI-285 are follow-ups of the username takeover fix (platform #741: a deleted user's engine username is never given out again, and the engines' `egret-admin` policies follow the active platform admins). KI-286 to KI-288 are engine gaps that **Who can read?** and the rule dialog state since platform #744 (verify a mask per user), and KI-289 was found in its live check. KI-290 to KI-295 come from the Wave 3 chart editor fixes (platform #743), which closed KI-008, KI-050, KI-053 to KI-056 and KI-058, and the alerts and reports fixes (platform #742), which closed KI-038, KI-039, KI-171 and KI-219 to KI-221; KI-295, an S2 bug in the Schedule tab that #742 found, was fixed by platform #745. KI-296 to KI-299 come from the Wave 3 Data API and Apps fixes (platform #746), which closed KI-025, KI-027, KI-031, KI-042, KI-176, KI-177 and KI-182 to KI-184; the workflows fixes (platform #747), which closed KI-014, KI-119, KI-121 to KI-123, KI-189 to KI-191, KI-193 and KI-226, and found KI-120 not to reproduce; and the Control Plane, Governance and settings fixes (platform #748), which closed KI-001, KI-007, KI-011, KI-016, KI-131, KI-132, KI-203, KI-204 and KI-212. KI-298 and KI-299 were found while writing the docs for them. KI-300 and KI-301 come from the Wave 3 small UX cleanup (platform #751), which closed KI-251, KI-280 to KI-282, KI-290 to KI-294 and KI-296 to KI-299 and left KI-283 open; KI-302 was found while writing the docs for it. The print page and wording fixes (platform #754) closed KI-283, KI-301 and KI-302 and found no new defect. KI-303 to KI-307 come from StarRocks enforcing whole-table classifications (platform #755), which closed KI-286 and KI-288; KI-303 was found while building it and the others in its live check. KI-308 to KI-310 come from classifications following a renamed table or column (platform #757), which closed KI-303: KI-308 and KI-309 are the related defects it recorded and left for a later package, and KI-310 is the cost of its hold. Same legend as above. **Open S1 items:** none (KI-013, KI-040, KI-059, KI-060, KI-112, KI-136, KI-159, KI-175 and KI-187 are closed). +Collected while writing the detailed pages for each area (October 2026), checked against the running demo instance and the product source. KI-154 to KI-213 come from re-verifying every area on the Docker demo stack (`just demo-up`, October 2026), which also closed or updated older rows (marked *Status* or *Docker demo stack*). KI-214 to KI-235 come from a whole-app UI consistency sweep of 78 screens (admin, light theme, 1440×900, same stack), then the dark theme, 768 and 390 px widths, the editors and a non-admin member. KI-236 to KI-246 come from the Wave 1 fixes of the Known-issues fix programme (October 2026), which also closed the rows marked *fixed in platform #…*. KI-247 to KI-259 come from the Wave 2 fixes (SQL Lab, charts, datasets and the Data API: platform #720, #721, #724 and #725), which closed or partly closed the rows they name. KI-260 to KI-266 come from the Wave 2b fixes (alerts and reports delivery, authentication and roles, engines and monitor: platform #726, #727 and #728), which closed the rows they name; KI-021 was closed as by design and KI-148 as no longer applying. KI-267 to KI-269 come from the Wave 3 page-structure fix (titles, page width, tabs, status badges, list links and detail headers: platform #734), which closed KI-214, KI-215, KI-216, KI-222, KI-223 and KI-224; KI-270 was seen during the docs screenshot re-shoot (docs #31). KI-271 to KI-279 come from the Wave 3 controls and states fixes (shared select and date picker, Notifications and the dashboard Filter settings panel: platform #736; the refusal inside the shell, sidebar and settings cleanup, small-screen layout: platform #737) and from the storage and Docker engine fix (platform #735), which closed KI-005, KI-012, KI-217, KI-218 and KI-235 (the last two partly as written), KI-225, KI-227, KI-230, KI-231, KI-233, KI-234, KI-236 and KI-263. KI-280 to KI-283 come from the Wave 3 dashboard fixes (platform #739), which closed KI-003, KI-064 to KI-069, KI-074, KI-075 and KI-168; the SQL Lab fixes of the same wave (platform #740) closed KI-082 to KI-085, KI-089, KI-091 to KI-094, KI-160 to KI-162, KI-228 and KI-229 and found no new defect. KI-303 to KI-307 come from StarRocks enforcing whole-table classifications (platform #755), which closed KI-286 and KI-288; KI-303 was found while building it and the others in its live check. KI-284 and KI-285 are follow-ups of the username takeover fix (platform #741: a deleted user's engine username is never given out again, and the engines' `egret-admin` policies follow the active platform admins). KI-286 to KI-288 are engine gaps that **Who can read?** and the rule dialog state since platform #744 (verify a mask per user), and KI-289 was found in its live check. KI-290 to KI-295 come from the Wave 3 chart editor fixes (platform #743), which closed KI-008, KI-050, KI-053 to KI-056 and KI-058, and the alerts and reports fixes (platform #742), which closed KI-038, KI-039, KI-171 and KI-219 to KI-221; KI-295, an S2 bug in the Schedule tab that #742 found, was fixed by platform #745. KI-296 to KI-299 come from the Wave 3 Data API and Apps fixes (platform #746), which closed KI-025, KI-027, KI-031, KI-042, KI-176, KI-177 and KI-182 to KI-184; the workflows fixes (platform #747), which closed KI-014, KI-119, KI-121 to KI-123, KI-189 to KI-191, KI-193 and KI-226, and found KI-120 not to reproduce; and the Control Plane, Governance and settings fixes (platform #748), which closed KI-001, KI-007, KI-011, KI-016, KI-131, KI-132, KI-203, KI-204 and KI-212. KI-298 and KI-299 were found while writing the docs for them. KI-300 and KI-301 come from the Wave 3 small UX cleanup (platform #751), which closed KI-251, KI-280 to KI-282, KI-290 to KI-294 and KI-296 to KI-299 and left KI-283 open; KI-302 was found while writing the docs for it. The print page and wording fixes (platform #754) closed KI-283, KI-301 and KI-302 and found no new defect. KI-303 to KI-307 come from StarRocks enforcing whole-table classifications (platform #755), which closed KI-286 and KI-288; KI-303 was found while building it and the others in its live check. KI-308 to KI-310 come from classifications following a renamed table or column (platform #757), which closed KI-303: KI-308 and KI-309 are the related defects it recorded and left for a later package, and KI-310 is the cost of its hold. KI-311 to KI-314 come from the catalog guard authorizing renames and commits by the tables they change (platform #758), which closed no row: KI-311 and KI-312 were found while building it, KI-313 and KI-314 in its live check. Same legend as above. **Open S1 items:** none (KI-013, KI-040, KI-059, KI-060, KI-112, KI-136, KI-159, KI-175 and KI-187 are closed). ### Data API, Alerts and Apps @@ -361,6 +361,10 @@ Collected while writing the detailed pages for each area (October 2026), checked | KI-308 | Governance / Grants | Security | S2 | A path-selector grant or rule (a grant, a mask or a row filter written on the table's path or one of its columns, not selected by a classification) and a hand-written Ranger policy stay on the old name when a table or column is renamed; only classifications follow it (platform #757). An allow is lost, which fails closed, but a **deny** no longer applies to the renamed table, and a path mask or row filter stops masking or filtering it, so data leaks. A table created later under the old name inherits all of them. *Workaround:* before a rename, write the deny, mask or filter on the new name too (or select by a classification, which follows the table), and remove the old-name grants and rules after it. (Found while building platform #757.) | | KI-309 | Control plane / Catalogs | Bug | S2 | A catalog grant on a table (**Control plane → Catalogs → \ → Roles & grants**, for example `analytics.orders · read`) stays on the old name when the table is renamed. A catalog client whose role has only that grant loses access to the renamed table, and gains it on a new table created under the old name. A grant on **All tables** of the namespace, or on the whole catalog, still applies while the table stays in that namespace. *Workaround:* add the grant on the new name and remove the old one. (Found while building platform #757.) | | KI-310 | Governance / Classifications | Limitation | S3 | A rename of a classified table or column waits until every bound engine serves the moved classifications. With StarRocks bound this costs about two policy polls (36 to 56 seconds measured with the 30-second poll), because the StarRocks plugin reports a version one poll after it downloads it; the limit is 90 seconds. Meanwhile another classified rename in the same catalog, and a schema change or undrop of a table there that carries a whole-table classification with a clearance, wait up to 30 seconds and are then refused with a 409 to retry. A bound engine whose plugins do not poll makes every classified rename fail after 90 seconds with *engines did not confirm the moved classifications within 90 s: …*. A rename run from SQL Lab gives up first, at SQL Lab's 60-second query limit (`engine.query_timeout_secs`), and shows *timeout: the query ran longer than the 60 s limit* instead of egret's reason; egret still refuses the rename and undoes the copy. (Measured in the live check of platform #757.) | +| KI-311 | Control plane / Catalogs | Security | S2 | **High severity.** A Catalog Client whose role holds only a **read** grant (on a table or on a namespace) receives vended S3 credentials that can also write and delete the table's files. egret's catalog hands out read-write-delete credentials for every table load, and also when the client sends no `X-Iceberg-Access-Delegation` header, so a reader can overwrite or delete the table's data and metadata files without any catalog write: in a measurement, a client with a table **read** grant made the table unreadable for everyone. The credentials are scoped to the loaded table's location: they cannot list, read or write other tables' files or the bucket root. A namespace **read** grant reaches every table in the namespace this way. *Workaround:* give **read** grants only to clients you would trust with write access to those tables. Being fixed in the next package. (Found while building platform #758, measured afterwards.) | +| KI-312 | Control plane / Catalogs | Security | S3 | Two catalog operations are wider than the grants suggest. Creating a namespace is allowed for a role with any **write** grant, because egret does not read the namespace from the request body, and listing every namespace in the catalog is allowed for a role with any grant. A grant on one table lets the role list every table and view name in that table's namespace. Names only: no table, data or credentials, and a namespace a role creates gives it no access. (Found while building platform #758.) | +| KI-313 | Control plane / Catalogs | Security | S3 | egret sets no time limit on reading the body of an Iceberg rename or commit. A client with a valid token that declares a body and sends nothing keeps the connection open with no answer (130 seconds and more measured). Memory stays capped (1 MiB for a rename, 16 MiB for a commit), and a request without a valid token is refused at once with 401. Behind the demo stack's edge, nginx buffers the body first, so the wait moves there. (Found in the live check of platform #758.) | +| KI-314 | Deploy / Demo stack | Bug | S2 | Behind the demo stack's edge on a port other than 80, `GET /catalog/v1/config` advertises its `uri` without the port (for example `http://egret.localtest.me/catalog` when the stack runs on 18780). PyIceberg follows the advertised address and fails with *connection refused* on port 80. *Workaround:* run the demo stack on port 80, or run the client on the stack's network and reach egret directly. *Steps:* `DEMO_PORT=18780`, then `load_catalog(uri="http://egret.localtest.me:18780/catalog", …).list_namespaces()`. The cause is not confirmed. (Found in the live check of platform #758.) | ## Not verified