Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .claude/skills/contributing-to-weaviate-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ weaviate-cli/
defaults.py # Dataclass defaults for all commands
utils.py # Shared helpers: get_client, pp_objects, parse_permission
commands/ # Click command definitions (one file per group)
create.py # create collection, tenants, data, backup, role, user, alias, replication
get.py # get collection, tenants, shards, backup, role, user, nodes, alias, replication
create.py # create collection, tenants, data, backup, role, user, alias, namespace, replication
get.py # get collection, tenants, shards, backup, role, user, nodes, alias, namespace, replication
update.py # update collection, tenants, shards, data, user, alias
delete.py # delete collection, tenants, data, role, user, alias, replication
delete.py # delete collection, tenants, data, role, user, alias, namespace, replication
query.py # query data, replications, sharding-state
restore.py # restore backup
cancel.py # cancel backup, replication
Expand All @@ -61,7 +61,8 @@ weaviate-cli/
data_manager.py # DataManager: ingest/update/delete data
backup_manager.py # BackupManager: backup/restore operations
role_manager.py # RoleManager: RBAC role operations
user_manager.py # UserManager: RBAC user operations
user_manager.py # UserManager: RBAC user operations (supports namespace-scoped users)
namespace_manager.py # NamespaceManager: namespace CRUD (Weaviate 1.38.0+)
node_manager.py # NodeManager: node information
shard_manager.py # ShardManager: shard operations
cluster_manager.py # ClusterManager: replication operations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ class CollectionManager:
self.client.collections.create(name=collection, ...)
```

Manager files: `collection_manager.py`, `tenant_manager.py`, `data_manager.py`, `backup_manager.py`, `export_manager.py`, `role_manager.py`, `user_manager.py`, `node_manager.py`, `shard_manager.py`, `cluster_manager.py`, `alias_manager.py`, `benchmark_manager.py`, `config_manager.py`
Manager files: `collection_manager.py`, `tenant_manager.py`, `data_manager.py`, `backup_manager.py`, `export_manager.py`, `role_manager.py`, `user_manager.py`, `namespace_manager.py`, `node_manager.py`, `shard_manager.py`, `cluster_manager.py`, `alias_manager.py`, `benchmark_manager.py`, `config_manager.py`

Managers handle:
- Input validation and error messages
Expand Down
53 changes: 49 additions & 4 deletions .claude/skills/operating-weaviate-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,10 @@ weaviate-cli [--config-file FILE] [--user USER] <group> <command> [--json] [opti

| Group | Description |
|-------|-------------|
| `create` | Create collections, tenants, data, backups, exports, roles, users, aliases, replications |
| `get` | Inspect collections, tenants, shards, backups, exports, roles, users, nodes, aliases, replications |
| `update` | Update collections, tenants, shards, data, users, aliases |
| `delete` | Delete collections, tenants, data, roles, users, aliases, replications |
| `create` | Create collections, tenants, data, backups, exports, roles, users, aliases, namespaces, replications |
| `get` | Inspect collections, tenants, shards, backups, exports, roles, users, nodes, aliases, namespaces, replications |
| `update` | Update collections, tenants, shards, data, users, aliases, namespaces |
| `delete` | Delete collections, tenants, data, roles, users, aliases, namespaces, replications |
| `query` | Query data (fetch/vector/keyword/hybrid/uuid), replications, sharding state |
| `restore` | Restore backups |
| `cancel` | Cancel backups, exports, and replications |
Expand All @@ -134,6 +134,9 @@ weaviate-cli [--config-file FILE] [--user USER] <group> <command> [--json] [opti
weaviate-cli create collection --collection MyCollection --replication_factor 3 --vector_index hnsw --vectorizer none --json
weaviate-cli get collection --json # List all
weaviate-cli get collection --collection MyCollection --json # Specific
weaviate-cli get collection --collection Movies --namespace myns --json # Operator: qualified target
weaviate-cli get collection --list-qualified-keys --json # Operator: list using full keys (no shell glob issue)
weaviate-cli get collection --namespace '*' --json # Same; * must be quoted in the shell
weaviate-cli update collection --collection MyCollection --description "Updated" --replication_factor 5 --json
weaviate-cli delete collection --collection MyCollection --json
weaviate-cli delete collection --all --json
Expand Down Expand Up @@ -284,6 +287,47 @@ Permission format: `action:target`. See [references/rbac.md](references/rbac.md)

**Shell quoting**: Permissions with wildcards must be quoted to prevent shell globbing: `-p 'crud_data:*'` (not `-p crud_data:*`, which fails in zsh with `no matches found`).

### Namespaces

```bash
weaviate-cli create namespace --name tenantswest --json
weaviate-cli create namespace --name tenantswest --home_node node1 --json # pin shards to a node
weaviate-cli get namespace --name tenantswest --json # shows home_node + read-only state
weaviate-cli get namespace --all --json
weaviate-cli update namespace --name tenantswest --home_node node2 --json # change home node only
weaviate-cli delete namespace --name tenantswest --json
```

Namespace names must be **3–36 lowercase alphanumeric characters starting with a letter** (regexp: `[a-z][a-z0-9]{2,35}`).

`--home_node` pins which cluster node holds the namespace's shards (optional on create, required on update). `update namespace` only changes the home node; existing live shards are not moved. `get namespace` surfaces the read-only `state` (`active`/`deleting`) and `home_node` when the server returns them.

**Prerequisite**: Requires Weaviate 1.38.0+. The CLI defers the version check to the python client.

#### Namespace-scoped DB users

On namespace-enabled clusters, a DB user is bound to a namespace by passing a
namespace-qualified id `<namespace>:<user>` as `--user_name` (there is no `--namespace`
flag; the server derives the namespace from the qualified id):

```bash
weaviate-cli create user --user_name tenantswest:scoped-user --json
weaviate-cli get user --user_name tenantswest:scoped-user --json # output includes "namespace"
```

#### Granting namespace permissions

```bash
weaviate-cli create role --role_name NsAdmin -p manage_namespaces:tenantswest --json
weaviate-cli assign permission -p manage_namespaces:tenantseast --role_name NsAdmin --json
```

Multiple namespaces in one permission: `-p manage_namespaces:ns1,ns2`. The wildcard form
`manage_namespaces` (without a name) is rejected — every grant must name an explicit
namespace.

See [references/namespaces.md](references/namespaces.md).

### Cluster & Nodes

```bash
Expand Down Expand Up @@ -461,6 +505,7 @@ When new commands or options are added to `weaviate-cli`:
- [references/backups.md](references/backups.md) -- Backup/restore options and notes
- [references/exports.md](references/exports.md) -- Collection export options and notes
- [references/rbac.md](references/rbac.md) -- Permission format, actions, and examples
- [references/namespaces.md](references/namespaces.md) -- Namespace CRUD, RBAC, and namespace-scoped users
- [references/cluster.md](references/cluster.md) -- Nodes, shards, replication operations
- [references/benchmark.md](references/benchmark.md) -- Benchmark options and output modes
- [references/config-management.md](references/config-management.md) -- Config patterns and decision tree
23 changes: 23 additions & 0 deletions .claude/skills/operating-weaviate-cli/references/collections.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,29 @@ Manage Weaviate collections (schemas).
weaviate-cli get collection --json
```

## Namespaced clusters (namespace-scoped user vs global operator)

With **namespace-scoped** credentials, `list_all` may return keys like `myns:Movies` while the API expects `Movies`. Omit `--namespace` (default): the CLI strips the prefix when fetching each collection.

With **global/operator** credentials, pass the qualified name. Either use a full `--collection myns:Movies`, or combine short name and namespace:

```bash
weaviate-cli get collection --collection Movies --namespace myns --json
```

When **listing** all collections as an operator across namespaces, use **`--list-qualified-keys`** so each server key is passed verbatim to `get` (avoids shell globbing on `*`). Alternatively quote: `--namespace '*'`.

```bash
weaviate-cli get collection --list-qualified-keys --json
weaviate-cli get collection --namespace '*' --json
```

To list only collections under one namespace:

```bash
weaviate-cli get collection --namespace myns --json
```

## Get Specific Collection
```bash
weaviate-cli get collection --collection "CollectionName" --json
Expand Down
124 changes: 124 additions & 0 deletions .claude/skills/operating-weaviate-cli/references/namespaces.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Namespaces Reference

Manage Weaviate namespaces, namespace-scoped DB users, and `manage_namespaces`
permissions. **Requires Weaviate 1.38.0+.**

## CRUD

```bash
weaviate-cli create namespace --name tenantswest --json
weaviate-cli create namespace --name tenantswest --home_node node1 --json
weaviate-cli get namespace --name tenantswest --json
weaviate-cli get namespace --all --json
weaviate-cli update namespace --name tenantswest --home_node node2 --json
weaviate-cli delete namespace --name tenantswest --json
```

### Namespace name rules

A namespace name must match the server-side validation:

- 3 to 36 characters long
- only lowercase letters and digits (regexp: `[a-z][a-z0-9]*`)
- must start with a letter

The CLI does not pre-validate the name; the server returns an error if it is invalid.

### Home node

`--home_node` (optional on `create`, required on `update`) pins which cluster node
holds the namespace's shards. It must be a current storage candidate; when omitted on
`create`, the cluster picks one automatically.

```bash
# Pin a home node at creation time
weaviate-cli create namespace --name tenantswest --home_node node1 --json

# Move future placements to a different node (existing live shards are NOT moved)
weaviate-cli update namespace --name tenantswest --home_node node2 --json
```

`update namespace` is backed by the `PUT /namespaces/{name}` endpoint and only changes
the home node — it is the single mutable field on a namespace.

### State

Namespaces carry a read-only `state` field (`active` or `deleting`). When the server
reports it, `get namespace` surfaces it: a `State:` line in text output and a `"state"`
key in JSON. `home_node` is shown the same way (a `Home node:` line / `"home_node"`
key). Both keys are omitted when the server does not return them (e.g. older clusters).

## Namespace-scoped DB users

On namespace-enabled clusters, a dynamic DB user is bound to a namespace by giving it
a **namespace-qualified id** of the form `<namespace>:<user>`. There is no separate
`--namespace` flag — the namespace is part of the `--user_name` value, which the CLI
forwards verbatim to `client.users.db.create(user_id=...)`. The server derives the
namespace from the qualified id.

```bash
# Create a user inside a namespace (qualified id "<namespace>:<user>")
weaviate-cli create user --user_name tenantswest:scoped-user --json
# Output JSON includes user_name and api_key.

# Inspect — for a global operator, text output adds a "Namespace:" line and JSON adds
# a "namespace" key (the server still returns it in the user response).
weaviate-cli get user --user_name tenantswest:scoped-user --json
weaviate-cli get user --all --json
```

To create a non-namespaced user, pass an unqualified `--user_name` (no colon) — this is
the normal form on clusters where namespaces are not enabled.

## RBAC: manage_namespaces

```bash
# Single namespace
weaviate-cli create role --role_name NsAdmin -p manage_namespaces:tenantswest --json

# Multiple namespaces in a single permission
weaviate-cli create role --role_name MultiNsAdmin -p manage_namespaces:tenantswest,tenantseast --json

# Add or remove a permission on an existing role
weaviate-cli assign permission -p manage_namespaces:tenantssouth --role_name NsAdmin --json
weaviate-cli revoke permission -p manage_namespaces:tenantswest --role_name NsAdmin --json
```

`get role` includes a `Namespaces Permissions` block in text output and a
`permissions.namespaces` array in JSON output.

### Restriction

The wildcard form `manage_namespaces` (no namespace name) is rejected — every grant
must name an explicit namespace. Use a comma-separated list to grant on several at
once:

```bash
-p manage_namespaces:ns1,ns2,ns3
```

## Workflow

1. `create namespace --name <ns>` — provision the namespace.
2. `create role --role_name <r> -p manage_namespaces:<ns>` — grant the management permission.
3. `create user --user_name <ns>:<u>` — create a namespace-scoped DB user (qualified id).
4. `assign role --role_name <r> --user_name <u>` — wire the role to the user.
5. Verify: `get namespace --name <ns>`, `get role --role_name <r>`, `get user --user_name <u>`.
6. Cleanup: `delete user` → `revoke role` (or `delete role`) → `delete namespace`.

## Notes

- The CLI delegates the version check (`>=1.38.0`) to the python client; older
servers return an error from the client itself.
- Namespaces are independent of multi-tenancy — they sit at the auth/account layer,
not the collection/tenant data layer.

## Collections and `get collection --namespace`

Collection API targets differ by credential type on namespace-enabled clusters:

- **Namespace-scoped DB user:** use `get collection` without `--namespace` (the CLI
strips `namespace:` from keys returned by `list_all` when loading details).
- **Global operator:** qualify the collection, e.g. `get collection --collection Movies --namespace myns`, or pass `--collection myns:Movies`. For listing all namespaces at once use **`get collection --list-qualified-keys --json`** (recommended; unquoted `--namespace *` is expanded by the shell to filenames). Equivalent: `get collection --namespace '*' --json`. For one namespace only: `get collection --namespace myns --json`.

See [collections.md](collections.md) for examples.
6 changes: 6 additions & 0 deletions .claude/skills/operating-weaviate-cli/references/rbac.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ Permissions use the format `action:target`. Multiple permissions can be specifie
| Cluster | `read_cluster` |
| Nodes | `read_nodes` |
| Backups | `manage_backups` |
| Namespaces | `manage_namespaces` (Weaviate 1.38.0+) |

**CRUD shorthands:** `crud_collections`, `crud_data`, `crud_tenants`, `crud_roles`, `crud_users`, `crud_aliases`, `cud_data`, `rd_data`, `cud_tenants`, `rd_tenants`, `rd_collections`, `cud_aliases`, `rd_aliases`

Expand All @@ -95,8 +96,13 @@ Any combination of `c`, `r`, `u`, `d` prefix letters works (e.g., `cr_collection
-p read_nodes:minimal
-p manage_backups:Movies
-p read_cluster
-p manage_namespaces:tenantswest
-p manage_namespaces:tenantswest,tenantseast
```

`manage_namespaces` requires an explicit namespace name (or comma-separated list of names);
the wildcard form `manage_namespaces` alone is rejected.

## Prerequisites

1. Cluster must have RBAC enabled
Expand Down
5 changes: 4 additions & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
weaviate-client>=4.21.0
# TODO: revert to a released version pin (e.g. weaviate-client>=4.x.x) once
# weaviate-python-client PR #2033 (https://github.com/weaviate/weaviate-python-client/pull/2033)
# merges and a release is cut.
weaviate-client @ git+https://github.com/weaviate/weaviate-python-client.git@26fe203e9b38b79551ae0972f023fdc42ff6cd8d
click==8.1.7
twine
pytest
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ classifiers =
include_package_data = True
python_requires = >=3.9
install_requires =
weaviate-client>=4.21.0
weaviate-client @ git+https://github.com/weaviate/weaviate-python-client.git@jose/namespaces
click==8.1.7
semver>=3.0.2
numpy>=1.24.0
Expand Down
76 changes: 76 additions & 0 deletions test/unittests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,79 @@ def test_main_commands_registered():
assert "update" in main.commands
assert "restore" in main.commands
assert "query" in main.commands


def test_update_namespace_registered():
assert "namespace" in main.commands["update"].commands


def test_create_namespace_forwards_home_node(cli_runner):
with (
patch(
"weaviate_cli.commands.create.get_client_from_context",
return_value=MagicMock(),
),
patch("weaviate_cli.commands.create.NamespaceManager") as mock_manager_cls,
):
result = cli_runner.invoke(
main,
["create", "namespace", "--name", "tenantswest", "--home_node", "node1"],
)
assert result.exit_code == 0
mock_manager_cls.return_value.create_namespace.assert_called_once_with(
name="tenantswest", home_node="node1", json_output=False
)


def test_update_namespace_forwards_home_node(cli_runner):
with (
patch(
"weaviate_cli.commands.update.get_client_from_context",
return_value=MagicMock(),
),
patch("weaviate_cli.commands.update.NamespaceManager") as mock_manager_cls,
):
result = cli_runner.invoke(
main,
["update", "namespace", "--name", "tenantswest", "--home_node", "node2"],
)
assert result.exit_code == 0
mock_manager_cls.return_value.update_namespace.assert_called_once_with(
name="tenantswest", home_node="node2", json_output=False
)


def test_update_namespace_requires_home_node(cli_runner):
result = cli_runner.invoke(main, ["update", "namespace", "--name", "tenantswest"])
# Missing required --home_node is a usage error.
assert result.exit_code == 2
assert "--home_node" in result.output


def test_create_user_forwards_qualified_user_name(cli_runner):
# A namespace-scoped user is created by passing a namespace-qualified id
# ("<namespace>:<user>") as --user_name; the CLI forwards it verbatim.
with (
patch(
"weaviate_cli.commands.create.get_client_from_context",
return_value=MagicMock(),
),
patch("weaviate_cli.commands.create.UserManager") as mock_manager_cls,
):
mock_manager_cls.return_value.create_user.return_value = "api-key"
result = cli_runner.invoke(
main, ["create", "user", "--user_name", "tenantswest:scoped"]
)
assert result.exit_code == 0
mock_manager_cls.return_value.create_user.assert_called_once_with(
user_name="tenantswest:scoped"
)


def test_create_user_rejects_namespace_option(cli_runner):
# The dropped --namespace flag must no longer be accepted (usage error).
result = cli_runner.invoke(
main, ["create", "user", "--user_name", "scoped", "--namespace", "tenantswest"]
)
assert result.exit_code == 2
assert "--namespace" in result.output
Loading
Loading