DocumentsLanceDB's Space
Namespace Metadata Operations and Access Permissions
Namespace Metadata Operations and Access Permissions
Type
Topic
Status
Published
Created
Jul 8, 2026
Updated
Jul 9, 2026
Updated by
Dosu Bot

Namespace Metadata Operations and Access Permissions#

LanceDB's namespace layer is a logical table-management abstraction (LanceNamespaceDatabase in Rust, LanceNamespaceDBConnection in Python) that sits above physical storage. It delegates table URI resolution, location registration, and credential vending to a LanceNamespace client, then uses standard Lance dataset machinery for actual data reads and writes.

Key operational fact: every open_table call triggers exactly one describe_table RPC to the namespace client. This is how the layer resolves a logical table name to a physical storage URI and obtains storage credentials.

open_tabledescribe_table call chain#

open_table(name)
  └─ LanceNamespaceDatabase::open_table [namespace.rs:520-535]
       └─ NativeTable::open_from_namespace [table.rs:2094]
            └─ DatasetBuilder::from_namespace [table.rs:2123]
                 └─ describe_table RPC ← fetches location + storage options

LanceNamespaceDatabase::open_table delegates directly to NativeTable::open_from_namespace, which calls DatasetBuilder::from_namespace with the assembled table_id (namespace_path + [name]). The from_namespace builder internally calls describe_table on the namespace client to fetch the physical table location and storage options — eliminating any need for callers to pre-fetch and merge those values.

Integration tests confirm the exact call count: test_namespace_open_table_with_provider asserts exactly one describe_table call per open_table invocation , and that subsequent reads on the same table object make no additional calls . For two independently opened tables, the count is exactly two .

Credential vending#

The namespace client can optionally return storage credentials inside every describe_table response — the vend_input_storage_options feature on DirectoryNamespace . This enables automatic, namespace-managed credential rotation.

Read path (open_from_namespace): DatasetBuilder::from_namespace picks up credentials directly from the namespace response. No separate provider setup is needed.

Write path (create_from_namespace): NativeTable::create_from_namespace explicitly creates a LanceNamespaceStorageOptionsProvider and wraps it in a StorageOptionsAccessor for automatic refresh. If initial static options exist, they are merged with the dynamic provider via StorageOptionsAccessor::with_initial_and_provider; otherwise with_provider is used alone .

Credential expiry and refresh: After the first open_table, credentials are cached on the table object. Once the expiry window elapses, the next data access triggers a fresh describe_table call. test_namespace_credential_refresh_on_read verifies that exactly one additional describe_table call occurs after expiry . The same pattern holds on the write path .

The vend_input_storage_options_refresh_interval_millis property on DirectoryNamespace controls how long vended credentials are considered fresh before a re-call is triggered .

describe_table in create_table paths#

describe_table is not always called; the pattern depends on mode:

Modedescribe_table called?Purpose
Create (default)NoUses declare_table to reserve location
OverwriteYes, onceRetrieves existing location so data can be overwritten in-place
ExistOkYes, onceChecks if table already exists; if found, opens it via open_from_namespace
Create (declare conflict)Yes, onceDisambiguates "declared but never written" vs. "fully realized" table before raising TableAlreadyExists

Tests validate these counts explicitly: create mode emits zero describe_table calls , and overwrite mode emits exactly one .

Write permissions and read-only access#

Issue #3633 (filed 2026-07-08, v0.33.0): lancedb.connect(...).open_table(...) against hf:// (Hugging Face) buckets with a read-only token would trigger a POST .../xet-write-token request and fail with HTTP 403 — even though lance.dataset(...) succeeded with the same token.

Root cause: listing-mode connections eagerly initialized a manifest-enabled DirectoryNamespace for the storage root. The manifest initialization requested a write credential (the Xet write token) during namespace construction, regardless of whether any write operation would subsequently be performed .

Contrast with direct Lance reads: lance.dataset() bypasses the namespace layer entirely, so no write token was requested.

Fixed in Lance v9.0.0-beta.19 (LanceDB PR #3635): DirectoryNamespace read paths no longer trigger manifest writes during initialization. Root-level operations (connect, open_table, table_names) now proceed without creating __manifest artifacts or requesting write credentials, enabling side-effect-free directory namespace opens with read-only tokens.

Key source files#

FileRole
rust/lancedb/src/database/namespace.rsLanceNamespaceDatabaseopen_table, create_table, and all namespace DB logic
rust/lancedb/src/table.rsNativeTable::open_from_namespace (line 2094), create_from_namespace (line 2304)
python/python/lancedb/namespace.pyLanceNamespaceDBConnection, connect_namespace, connect_namespace_async
python/python/tests/test_namespace_integration.pyIntegration tests tracking describe_table / declare_table call counts and credential refresh
Namespace Metadata Operations and Access Permissions | Dosu