> For the complete documentation index, see [llms.txt](https://docs.opendatadiscovery.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.opendatadiscovery.org/features/management/namespaces.md).

# Namespaces

A **Namespace** is a logical scoping unit applied across the catalog's taxonomy and resource graph. Every Term and Tag carries an optional namespace; every Data Source and Collector binds to one; Data Entity Groups can inherit a namespace through their parent. Namespaces are the catalog-side analogue of a team-or-domain label — operators use them to keep two teams' "customer" terms from colliding, to keep two teams' data sources visually separated in the catalog, and to scope collector ingestion to a known taxonomy.

This page covers the create / update / soft-delete / list lifecycle, the three permission gates, the cascade-on-delete contract, the case-sensitivity rule, the audit-silence shape Namespace CRUD shares with Owners, and the **four-sister-service auto-create side-door** that mints namespace rows without `NAMESPACE_CREATE`.

## Where to find it

Open the [Management](/features/management.md) page → **Namespaces** tab. The page lists every namespace with a pagination header and a free-text filter; the right-side panel renders the create form. Operators with `NAMESPACE_CREATE` see the **Add namespace** affordance; operators with `NAMESPACE_UPDATE` see an in-place edit on each row; operators with `NAMESPACE_DELETE` see a remove affordance.

## Lifecycle

Five operations exist on the Namespace surface:

| Operation                                                      | Endpoint                      | Permission                                       |
| -------------------------------------------------------------- | ----------------------------- | ------------------------------------------------ |
| List namespaces (paginated, optional `query` substring filter) | `GET /api/namespaces`         | Any authenticated user; anonymous under DISABLED |
| Get a single namespace                                         | `GET /api/namespaces/{id}`    | Any authenticated user; anonymous under DISABLED |
| Create a namespace                                             | `POST /api/namespaces`        | `NAMESPACE_CREATE`                               |
| Update a namespace                                             | `PUT /api/namespaces/{id}`    | `NAMESPACE_UPDATE`                               |
| Soft-delete a namespace                                        | `DELETE /api/namespaces/{id}` | `NAMESPACE_DELETE`                               |

The read endpoints are not gated by a custom-permission entry — the platform's security wiring leaves them at the default `.authenticated()` level. Under `auth.type=DISABLED` they are reachable anonymously (see [DISABLED authentication](/configuration-and-deployment/enable-security/authentication/disabled-authentication.md)).

## Case sensitivity

Namespace names are **case-sensitive**. `finance` and `Finance` are two distinct rows in the `namespace` table — an operator who creates `Finance` and a teammate who creates `finance` end up with two parallel namespaces that share no relations. The list endpoint's `query` parameter applies a case-insensitive substring match for the suggestion list, but resolution against the canonical row is by exact-string match.

Treat namespace names as a controlled vocabulary that benefits from a documented naming convention — `snake_case` and `lowercase-with-hyphens` are both common in deployments we have seen. Drift between case-variants is one of the most common Namespace-directory pollution shapes.

## Soft-delete and reincarnation

The Namespace delete path is a **soft delete** — the `DELETE /api/namespaces/{id}` endpoint sets `deleted_at = NOW()` on the row rather than removing it from the table. The `namespace` table carries a partial-unique index on `name` that scopes uniqueness to rows where `deleted_at IS NULL`; after a soft-delete, the name slot is free and an operator can create a new namespace with the same name. The previously-deleted row is preserved with its original `id`; downstream auditing tables that reference the namespace by id retain their integrity.

The reincarnation pattern is operationally useful for "reset this namespace" workflows — an operator who wants to clear a namespace's history can soft-delete it and immediately create a new namespace with the same name. The new namespace has a different `id`; any prior relations (terms, data sources, collectors) remain attached to the soft-deleted row and do not migrate.

## Cascade-on-delete guard

Namespace delete is blocked when any of four referent tables still reference the namespace. The platform's service layer zips four existence-predicates and rejects the operation if any returns true:

| Referent table                   | Operator-visible referent                        | Where to clear it                                                                                                                    |
| -------------------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ |
| `data_source` (live rows)        | A Data Source registered under this namespace    | Re-assign the source to a different namespace via [Management → Datasources](/features/management.md), or remove the source entirely |
| `collector` (live rows)          | A Collector registered under this namespace      | Re-assign the collector or remove it via [Management → Collectors](/features/management.md)                                          |
| `term` (live rows)               | A Glossary Term scoped to this namespace         | Re-assign or delete the term via the [Business Glossary](/features/data-glossary/business-glossary.md)                               |
| `data_entity` (non-deleted rows) | A live data entity ingested under this namespace | Re-configure the collector's `namespace_name` and re-ingest, or soft-delete the entities                                             |

When any predicate returns true, the platform returns `CascadeDeleteException` (HTTP 400, error code `USR004`) and the namespace remains live. The error message reads "Namespace cannot be deleted: there are still resources attached" — it does not enumerate which of the four classes is blocking the delete. The operator has to walk each referent class themselves.

{% hint style="warning" %}
**The cascade guard is best-effort, not transactional.** The delete path is not wrapped in a transaction, so the four existence-checks and the soft-delete that follows run as separate statements at `READ COMMITTED` with no lock held on the namespace. If a new referent — a data source, collector, term, or ingested data entity scoped to this namespace — is created in the brief window *after* the checks pass and *before* the soft-delete commits, the delete still succeeds and the new referent is left pointing at a soft-deleted namespace. This is a race only under concurrent writes (an operator deleting a namespace at the same moment another operator or a collector ingest attaches something to it); for the common case of a single operator clearing referents by hand it behaves exactly as the table above describes. If you need a hard guarantee, quiesce writes to the namespace before deleting it.
{% endhint %}

## Auto-create side-door

The Namespace surface carries a load-bearing structural quirk: **four sister services on the platform mint Namespace rows through the side-door `NamespaceServiceImpl.getOrCreate(name)`** when their request body carries a non-empty `namespace_name` field. The four callers are:

| Sister service            | Endpoint                                             | Gating permission for the parent operation |
| ------------------------- | ---------------------------------------------------- | ------------------------------------------ |
| Data Source service       | `POST /api/datasources`, `PUT /api/datasources/{id}` | `DATA_SOURCE_CREATE`, `DATA_SOURCE_UPDATE` |
| Term service              | `POST /api/terms`                                    | `TERM_CREATE`                              |
| Collector service         | `POST /api/collectors`                               | `COLLECTOR_CREATE`                         |
| Data Entity Group service | `POST /api/dataentitygroups`                         | `DATA_ENTITY_GROUP_CREATE`                 |

Each of these calls `getOrCreate(name)`, which does `getByName(name).switchIfEmpty(createByName(name))` — the service silently inserts a new Namespace row if the name does not already exist. The caller does **not** need to hold `NAMESPACE_CREATE`; holding any one of the four parent permissions is sufficient to mint a Namespace row that becomes immediately visible to every authenticated user via the catalogue read.

{% hint style="warning" %}
**The four-sister-service side-door collapses `NAMESPACE_CREATE` with four parent permissions.** Operators expecting `NAMESPACE_CREATE` to be the gate for namespace creation — as the [Permissions page](/configuration-and-deployment/enable-security/authorization/permissions.md) describes it — discover the side-door when their namespace directory proliferates with junk rows (typos, organic operator typing of distinct strings for the same intent through one of the four side-door endpoints).

**Mitigation today.** Treat `DATA_SOURCE_CREATE`, `DATA_SOURCE_UPDATE`, `TERM_CREATE`, `COLLECTOR_CREATE`, and `DATA_ENTITY_GROUP_CREATE` as implicitly granting namespace-creation rights when their request bodies carry `namespace_name`. Audit the Namespaces list growth on a cadence — typo-introduced duplicates and case-variants are the most common operator-visible drift shape (see [Case sensitivity](#case-sensitivity) above). If strict separation between Namespace creation and the four sister-service operations is a deployment requirement, treat the `namespace_name` form field as a closed allowlist at the network-perimeter layer rather than relying on platform RBAC.

The side-door is not a candidate for a one-commit upstream fix — closing it would either break the existing collector-ingestion workflow (which currently expects the side-door for first-time namespace bootstrap), or require a finer-grained `NAMESPACE_CREATE_VIA_RESOURCE` permission that adds UX complexity. Both are product-level decisions; the operator-side caveat is the right immediate ship.
{% endhint %}

A related sibling pattern exists on the [Owner](/configuration-and-deployment/enable-security/authorization/owners.md) surface — Owner CRUD has its own side-door cluster through three resource-creation endpoints. The Namespace cluster is the canonical example because four distinct sister services touch it, but the architectural pattern (auto-create on `getOrCreate(name)`-style helpers) is general.

## Activity trail

**Namespace CRUD emits no Activity Feed event.** Two independent facts of the platform's design both block it:

1. **No event type exists.** The Activity Feed's event-type catalogue has no `NAMESPACE_*` member at all — there is simply no type the platform could emit for a namespace create, update, or delete.
2. **The activity table requires a data-entity anchor.** Even if an event type existed, the activity table is schema-anchored to `data_entity_id NOT NULL` — every emitted event must reference a single data entity (see [Activity Feed → Scope](/features/active-platform-features/activity-feed.md#known-caveats)). Namespace lifecycle operations have no data-entity anchor.

Closing one of these alone would not make Namespace CRUD auditable; both would have to change. The same shape applies to Owner CRUD, Role CRUD, Policy CRUD, and every other RBAC and taxonomy mutation; see [Audit trail scope](/configuration-and-deployment/enable-security/audit-trail-scope.md) for the platform-wide audit-coverage matrix and the compensating-controls catalogue.

For compliance regimes that need who-changed-what-when on Namespaces, instrument the audit externally — an API-gateway access log records every authenticated mutation on `/api/namespaces*`; the PostgreSQL WAL via `pgaudit` records the `namespace` row writes. The audit-of-occurrence lives outside the platform until both the missing event type and the schema-tier `data_entity_id NOT NULL` anchor are addressed.

## Where to next

* [Management](/features/management.md) — the parent page; covers the tab-visibility model and the read-collaborative posture across the eight non-Associations Management areas.
* [Permissions](/configuration-and-deployment/enable-security/authorization/permissions.md) — the canonical home for `NAMESPACE_CREATE`, `NAMESPACE_UPDATE`, `NAMESPACE_DELETE` and the four sister-service permissions the side-door collapses against.
* [Activity Feed → Scope](/features/active-platform-features/activity-feed.md#known-caveats) — the canonical home for the audit-silence pattern Namespace CRUD shares with Owners, Roles, and Policies.
* [Audit trail scope](/configuration-and-deployment/enable-security/audit-trail-scope.md) — the compliance-facing summary of what the platform audits today and the external instrumentation patterns for the silent-mutation surfaces.
* [Business Glossary](/features/data-glossary/business-glossary.md), [Manual Object Tagging](/features/data-discovery/tagging.md) — the two taxonomy surfaces that scope themselves to a Namespace; one canonical place to clear referents when preparing for a Namespace delete.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.opendatadiscovery.org/features/management/namespaces.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
