Catalog identity, storage, and access are different things

A catalog helps you name and find data. It does not, by itself, grant permission to read that data. This distinction matters when the same table name appears in two domains or catalogs, and when an original file exists in storage before it is safe to query.

The three responsibilities

System What it decides What it does not decide
Griot resource catalog Which workspace, catalog, domain, table or bucket a name identifies; its lifecycle, contract and lineage Whether the presence of a storage object grants a user access
Griot identity and enforcement Who the caller is, which workspace they belong to, and whether the operation satisfies its resource and contract rules Where Iceberg stores a table's snapshots
Iceberg catalog and object storage Physical table metadata, snapshots and object bytes Griot user roles, contract purpose or bucket visibility policy

For example, default.sales.orders and finance.sales.orders can be different Griot resources even though both are called orders. A table's Iceberg namespace includes tenant, catalog, domain and dataset for new tables, but that namespace is a location and identity, not an authorization check. A governed query still goes through Griot's read enforcement. A bucket is different from a table: it preserves original files and relative paths; explicitly creating a table from a bucket file records its source lineage.

What each boundary currently enforces

  • The public API derives tenant scope from a validated caller credential. T02 uses tenant-scoped schemas for catalog records. Bucket-source ingestion checks the actor's tenant and raw-steward authority, the registered file, and its stored hash and size before it can become a table source.
  • Generic T02 object operations require both a tenant UUID and slug. T02 checks that the slug's bucket has an active permanent claim for that UUID before reading, listing, deleting, purging or reconciling objects. The claim prevents a valid-looking slug from selecting another tenant's bucket. It does not authorize an end user to read a particular governed resource; that decision remains at the Griot API/enforcement boundary.
  • Catalog-qualified pointers and contract records distinguish same-named resources in different catalogs. An ambiguous older, unqualified address must be rejected rather than guessed. Deletion and sidecar cleanup need the same qualified identity so deleting one resource does not remove another catalog's profile or quarantine record.
  • Governed unstructured document text and original-file endpoints currently deny with a typed 403 because they cannot yet obtain a complete contract-bound read grant. A governed catalog row alone is insufficient. Quarantine inspection remains restricted to a raw steward. This is a safe denial, not a completed document-read feature.

These are source-verified behaviors, not a claim that every deployment has passed live isolation testing. See contracts and queryability for how a table becomes queryable and members and access for credential types.

Still to decide or verify

Bucket metadata visibility is a product policy decision: the current same-workspace listing can expose file paths, classification, hashes and internal object keys to admitted callers. Griot needs an explicit rule for who may list that metadata and should avoid returning internal storage coordinates in public responses. Do not infer a bucket-specific grant merely from workspace membership.

The governed document routes need a real contract-bound, purpose-aware read decision before original bytes or extracted text can be enabled again. A steward shortcut or a purpose string alone would not satisfy that requirement. The local integration has source-level denial tests, but not a live authorized document-read success test because that grant does not exist yet.

Before rolling the object identity guard into a populated environment, check that existing tenant buckets have claims. T02's startup backfill derives claims from each tenant's own object ledger; it does not safely infer ownership from the slug on an incoming read. A nonempty bucket lacking a claim needs operator investigation against authoritative tenant and ledger evidence, not an automatic claim or a slug-only fallback. Reindexing or migrating a renamed catalog/domain likewise needs a qualified mapping of pointers, sidecars, lineage and object prefixes; changing an Iceberg namespace alone does not migrate Griot identity or policy.

Evidence and release gates

This page reflects the integration source reviewed at 208e22c71. In-source checks include zone-t/t02-storage-authority/tests/pointer_catalog_identity.rs for twin catalog pointers, bucket_claim.rs for active/mismatched/released/unclaimed claims, and zone-k/k01-api-gateway/tests/unit/test_governed_document_guard.py for synthetic denied document reads. The code-backed review is recorded in outputs/catalog-security-review.md. Those tests establish specific boundaries, not an absolute “no leaks” guarantee. Release acceptance still requires clean-checkout integration tests for same names across tenants, catalogs and domains; nonsteward bucket metadata policy tests; and live negative document-byte checks after deployment.