
As an organization scales, so does the need for greater granularity over data access controls and compartmentalization across different domains.
Namespaces in OpenBao deliver just that: the ability for a single instance of OpenBao to support multi-tenant secrets management. With the release of OpenBao 2.3.1, you can store secrets in separate namespaces (independent OpenBao contexts) with their own access control policies, authorization strategies, plugins, and additional metadata and configuration.
A single instance of OpenBao can now handle complex configurations requiring isolation of secrets per app or per team/department.
In this post, we'll explore:
Namespaces have long existed in OpenBao's codebase, at least in spirit, as API stubs. In late September 2024, a GitHub issue compiling a roadmap for the project included a mention of implementing namespaces support. In December 2024, OpenBao contributors Peter Bi and Alex Scheel proposed namespaces to the project in the form of a Request For Comments (RFC). The RFC outlines what namespaces should look like both at a high level and at an implementation level.
Through collaboration between multiple contributors — including Adfinis — the implementation of namespaces in the OpenBao codebase began late-2024 and progressed into mid-2025.
Namespaces are designed with the following considerations:
Namespaces can be manipulated directly through the /sys/namespaces API path, or the CLI via the bao namespace sub-command. This provides flexibility to automate operations on namespaces using shell scripts, configuration management tools such as OpenTofu or Ansible, web requests from another machine, or self-service portals (e.g. Backstage).
HTTP API operations can be scoped to a specific namespace by passing a header. CLI commands can be scoped by setting an environment variable, passing the -namespace flag, or when “implicitly” defined by the accessed path.
As an open source project under the guardianship of the Linux Foundation, the implementation of namespaces in OpenBao was developed from the ground up, in the open. Bringing namespace support from concept to production necessitated close collaboration between several community contributors. This included Adfinis, Jan Martens and Reply, who supported its development through a dedicated Working Group, joint live coding session, pull requests, code reviews, and testing.
Furthermore, the initiative's success was critically dependent on the contributions of Wavelo and Tucows. Their forward-thinking financial support for Adfinis, combined with their rigorous testing and bug reporting, proved essential.
Implementation of namespaces kicked off in February 2025, with pull request #896 laying the groundwork for further iteration submitted by Tabilet Process. This added an initial implementation in the form of a NamespaceStore object. After a series of in-depth code reviews, it was merged into the OpenBao repository on February 17th.
Following the merge, the newly introduced NamespaceStore implementation was refined further by Alex in pull request #1026 to improve conformity with the original RFC, as well as to prepare for the addition of child namespace support down the line.
Up until this point, the implementation of namespaces was not exposed as a public interface in OpenBao — it existed strictly as an internal implementation. This changed in late February and March, with a series of pull requests. Key areas of OpenBao functionality were made namespace aware, including:
While developing namespaces, the community made an important set of decisions: All parts — such as identity, policy, token stores, cubbyhole, plugins, and the expiration manager — exist internally by namespace path and are completely isolated. This is a prerequisite for making OpenBao more flexible and scalable in the future, as it opens up new opportunities for infrastructure changes like sharding based on namespaces or federations. Separating stores and caches could also improve the performance of read requests, which is especially important under high load.
Finally, after a number of contributions to refine behavior and API output, namespaces were made generally available on June 25, 2025, as part of the OpenBao 2.3.1 release.
Assuming the reader is familiar with OpenBao, and to illustrate the basics of how namespaces in OpenBao work, let's start by spinning up an OpenBao server in development mode:
$ bao server -dev -dev-root-token-id="root"
To get started, let's create our first namespace, my-app:
$ bao namespace create my-app
Key                Value
---                -----
custom_metadata    map[]
id                 rCXKPv
locked             false
path               my-app/
tainted            false
uuid               <uuid>
We can confirm that the namespace exists by using the list subcommand:
$ bao namespace list
Keys
----
my-app/
Now that we have a namespace, let's mount a secrets engine. For illustrative purposes, we'll enable a key-value engine at the /example path:
$ bao secrets enable -namespace=my-app -path=example kv-v2
Success! Enabled the kv-v2 secrets engine at: example/
Let's store a username/key pair in our store:
$ bao kv put -namespace=my-app example/auth username=jdoe key=1234
== Secret Path ==
example/data/auth
======= Metadata =======
Key                Value
---                -----
created_time       <time>
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1
Let's try to get the secret as we would've before OpenBao 2.3.1:
$ bao kv get example/auth
Error making API request.
URL: GET http://127.0.0.1:8200/v1/sys/internal/ui/mounts/example/auth
Code: 403. Errors:
* preflight capability check returned 403, please ensure client's policies
  grant access to path "example/auth/"
Because we're running a command without specifying the -namespace flag, OpenBao attempts to look up the mount path in the context of the root namespace — a mount path which doesn't exist. Furthermore, we can see that the mount path we created earlier can't be found when we omit the -namespace flag:
$ bao secrets list
Path          Type         Accessor              Description
----          ----         --------              -----------
cubbyhole/    cubbyhole    cubbyhole_c2ebca02    per-token private secret storage
identity/     identity     identity_215760b0     identity store
secret/       kv           kv_b4c5c7d5           key/value secret storage
sys/          system       system_22c708fa       system endpoints used for control,
                                                 policy and debugging
However, once we specify the correct namespace, we can see that the mount path exists:
$ bao secrets list --namespace=my-app
Path          Type            Accessor              Description
----          ----            --------              -----------
cubbyhole/    ns_cubbyhole    cubbyhole_f00f9c71    per-token private secret storage
example/      kv              kv_341ae3ba           n/a
identity/     ns_identity     identity_96022535     identity store
sys/          ns_system       system_5db5218a       system endpoints used for control, 
                                                    policy and debugging
It also works if we attempt to get our secret by specifying the namespace we created it under:
$ bao kv get --namespace=my-app example/auth
== Secret Path ==
example/data/auth
======= Metadata =======
Key                Value
---                -----
created_time       <time>
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1
====== Data ======
Key         Value
---         -----
key         1234
username    jdoe
During the implementation, the community decided to apply stricter identity segregation between namespaces than Vault Enterprise. Because the Identity Store and MemDBs are now per-namespace, group inheritance for policies no longer works across namespaces. For example, attaching a group in the root namespace and adding groups from child namespaces as members no longer works.
Some OpenBao users rely on this behavior (see openbao/openbao#1110, so a config flag unsafe_cross_namespace_identity was introduced.
By default OpenBao is more isolated (unsafe_cross_namespace_identity = false). When enabled, it results in weaker multi-tenancy but restores compatibility to Vault Enterprise; the identity subsystem uses a single MemDB, allowing cross-namespace lookups and group inheritance.
This flag eases adoption and migration for customers who depend on the Vault Enterprise behavior.
In this post, we've explored the design considerations for OpenBao's recently released namespaces functionality, and delved into how the implementation was developed in the open.
If you're interested in exploring how OpenBao can help you protect and manage secrets across your organization, Adfinis is ready to assist you. As co-maintainer and Technical Steering Committee (TSC) member of the OpenBao project, we have the experience to plan, build, run and provide enterprise support for adopting open, simple, and reliable secrets management.
Reach out to us today and let's explore how we can help you adopt OpenBao in your organization.