# Metastore (/docs/data/developer-workspace/metastore)



BullSequana AI's metastore catalogs tabular data for each tenant. Tables belong to
datasets, and datasets belong to spaces, the same personal and shared spaces used
elsewhere in Data. Access to data assets is governed centrally by a tenant administrator.

## What the metastore provides [#what-the-metastore-provides]

* A per-tenant catalog that holds every space, dataset, and table that tenant owns.
* Personal spaces for a user's own data and shared spaces for a team's data, so ownership
  stays clear.
* A single point of access control: granting a role on a space, dataset, or table applies
  consistently, so the grant a tenant administrator makes is the grant that governs the
  catalog.

## Access roles [#access-roles]

A tenant administrator grants a role on a space, dataset, or table. The supported roles depend on the resource scope:

| Role          | What it allows                                                    |
| ------------- | ----------------------------------------------------------------- |
| `owner`       | Full control, including deleting the object                       |
| `editor`      | Create tables in a dataset, or modify an existing table           |
| `contributor` | Write to an existing dataset (not available on individual tables) |
| `viewer`      | Read data                                                         |
| `discoverer`  | See that the object exists                                        |

## Example: give a colleague access to a dataset [#example-give-a-colleague-access-to-a-dataset]

Say a tenant administrator wants to let a colleague, `alice`, create and query tables in
a shared dataset called `fraud`.

### Before you start [#before-you-start]

The platform operator must enable the metastore integration and configure this tenant's metastore project. The `default` warehouse and `fraud` dataset in this example must already exist; a role grant does not create them. Substitute your site's warehouse, dataset, and user identity.

The examples use `user:alice@acme.com` because the metastore subject claim is `email`. If the installation uses another subject claim, use that claim's exact value instead. Integrated metastore grants accept direct `user:<id>` subjects, not group subjects.

### 1. Grant access [#1-grant-access]

The administrator grants Alice the `editor` role on the `fraud` dataset from their own
Developer Workspace notebook, the same way any user in the tenant would call the
platform's data helper library.

<details>
  <summary>
    Example: grant the role
  </summary>

  ```python
  import bsqai_data
  import requests

  TUPLES_URL = "http://tuples.tuples.svc.cluster.local:8080"
  token = bsqai_data.get_token()

  response = requests.post(
      f"{TUPLES_URL}/api/v1/datasets/default/fraud/roles",
      headers={"Authorization": f"Bearer {token}"},
      json={"subject": "user:alice@acme.com", "relation": "editor"},
  )
  print(response.status_code)  # 201
  ```
</details>

A `201` response confirms the grant was recorded. With the tenant metastore integration configured, the request updates both authorization stores. Verify the effective access through Alice's catalog operation in the next step; a tenant without metastore integration writes only to the Data authorization store.

### 2. Use the access [#2-use-the-access]

Alice opens a Developer Workspace notebook. The platform authenticates her automatically
using her existing platform sign-in, so there is no separate metastore login. From the
notebook, she requests a catalog client for the tenant's data, confirms she can see
`fraud`, and creates a table inside it.

<details>
  <summary>
    Example: create a table
  </summary>

  ```python
  import bsqai_data
  from pyiceberg.schema import Schema
  from pyiceberg.types import NestedField, StringType, DoubleType, TimestampType

  catalog = bsqai_data.get_catalog(warehouse="default")
  print(catalog.list_namespaces())  # includes ("fraud",)

  schema = Schema(
      NestedField(field_id=1, name="transaction_id", field_type=StringType(), required=True),
      NestedField(field_id=2, name="amount", field_type=DoubleType(), required=True),
      NestedField(field_id=3, name="event_time", field_type=TimestampType(), required=False),
  )

  table = catalog.create_table("fraud.transactions", schema=schema)
  print(table)
  ```
</details>

### 3. Revoke access [#3-revoke-access]

Removing the role works the same way, with a `DELETE` request instead of a `POST`. This removes the direct grant; independent or inherited grants can still provide access. Verify the expected denial after revocation.

<details>
  <summary>
    Example: revoke the role
  </summary>

  ```python
  response = requests.delete(
      f"{TUPLES_URL}/api/v1/datasets/default/fraud/roles",
      headers={"Authorization": f"Bearer {token}"},
      json={"subject": "user:alice@acme.com", "relation": "editor"},
  )
  print(response.status_code)  # 204
  ```
</details>

## Diagnose a refused or incomplete change [#diagnose-a-refused-or-incomplete-change]

* `400`: the subject or role is unsupported at that scope. For example, a group subject or `contributor` on an individual table is rejected before either store changes.
* `404`: the target warehouse, namespace, or table does not exist in the metastore.
* `401` or `403`: check the caller's authentication, selected tenant, and authority to manage the resource.
* `500`: ask the platform operator to inspect the authorization-service logs and compare both stores before treating the change as complete.

Integrated changes write to the metastore first, then the Data authorization store. A failure in the second write triggers a best-effort compensating change; the two stores do not share a transaction. If compensation also fails, an operator must reconcile them. Grant and revoke requests are idempotent, but retrying alone is not evidence that a reported inconsistency has been resolved.

## Related pages [#related-pages]

* [Tuples component](/docs/data/components/tuples)
* [Lakekeeper component](/docs/data/components/lakekeeper)
* [Multi-tenancy and tiered access in Data](/docs/data/multi-tenancy-and-tiered-rbac)
