How to Configure and Use Namespace Isolation in CoSky

CoSky isolates resources by namespace—a logical tenant identifier prefixed to every Redis key and REST API path—enabling multi-tenant configuration and service discovery through the spring.cloud.cosky.namespace property or runtime context switching.

Namespace isolation in CoSky provides the foundation for multi-tenancy in distributed systems. The ahoo-wang/cosky repository implements this pattern across three architectural layers: configuration properties, runtime thread-local context, and REST API path variables. Each namespace maintains independent config data, service discovery metadata, and RBAC policies within the same Redis backend.

Understanding Namespace Isolation Architecture

CoSky applies namespace isolation at three distinct layers to ensure complete tenant separation.

Configuration Layer

The spring.cloud.cosky.namespace property sets the default tenant identifier for the entire application. When omitted, CoSky falls back to Namespaced.DEFAULT (value: "cosky"). This value is defined in CoSkyProperties.kt and represents the root of all key prefixes stored in Redis.

Runtime Context Layer

At startup, CoSkyAutoConfiguration copies the property value into NamespacedContext.namespace, a thread-local holder that downstream components reference. All internal services—including ConfigService, DiscoveryService, and NamespaceService—read the current namespace from this context, ensuring operations remain scoped to the active tenant.

REST API Layer

Every tenant-scoped endpoint includes a {namespace} path variable under the /v1/namespaces/{namespace} prefix. Controllers extract this variable and forward it to the service layer, as implemented in RequestPathPrefix.kt and consumed by controllers like ServiceController.kt.

Configuring the Default Namespace

Set the default namespace in application.yaml (or bootstrap.yaml if required before the Spring context starts):

spring:
  cloud:
    cosky:
      # The tenant identifier used when no explicit namespace is provided

      namespace: my-company

When this property is present, CoSkyAutoConfiguration.kt executes:

// CoSkyAutoConfiguration.kt (excerpt)
NamespacedContext.namespace = coSkyProperties.namespace

All components using NamespacedContext.namespace automatically operate within the my-company tenant. This configuration affects config resolution, service registration, and permission checks throughout the application lifecycle.

Switching Namespaces at Runtime

For multi-tenant SaaS scenarios requiring dynamic tenant switching, inject the NamespaceService bean:

@Service
class TenantInitializer(
    private val namespaceService: NamespaceService
) {
    fun switchTo(tenant: String): Mono<Void> =
        namespaceService.setNamespace(tenant)   // Updates thread-local context
}

The NamespaceService API (defined in NamespaceService.kt) provides additional management operations:

// List all registered namespaces
namespaceService.namespaces.subscribe { println(it) }

// Permanently delete a namespace and all its data
namespaceService.deleteNamespace("old-tenant").subscribe()

Runtime switching changes the thread-local NamespacedContext immediately, causing subsequent operations to target the new tenant's keyspace in Redis.

Using the REST API for Namespace Operations

All tenant-aware endpoints reside under /v1/namespaces/{namespace}. The REST API defined in RequestPathPrefix.kt exposes the following operations:

  • List namespaces: GET /v1/namespaces returns all registered tenants.
  • Create namespace: POST /v1/namespaces with body { "namespace": "new-tenant" }.
  • Delete namespace: DELETE /v1/namespaces/{namespace} removes the tenant and all associated data.
  • Config management: GET/PUT/DELETE /v1/namespaces/{namespace}/configs/{configId} handles tenant-scoped configuration.
  • Service discovery: GET /v1/namespaces/{namespace}/services lists services within the tenant.

Controllers extract the namespace from the path variable and pass it to the service layer. For example, ServiceController.kt implements:

// ServiceController.kt (excerpt)
@GetMapping("/{namespace}/services")
fun getServices(@PathVariable namespace: String): Mono<List<String>> =
    discoveryService.getServices(namespace).collectList()

Summary

Frequently Asked Questions

What is the default namespace in CoSky?

If you do not specify spring.cloud.cosky.namespace, CoSky uses Namespaced.DEFAULT with the value "cosky". This global default ensures backward compatibility while allowing explicit tenant configuration.

Can I change the namespace after the application starts?

Yes. Inject NamespaceService and call setNamespace(tenant) to update the thread-local context. This switches the tenant scope for subsequent operations without restarting the application, enabling multi-tenant SaaS architectures.

How does namespace isolation work in Redis?

CoSky prefixes every stored key with the namespace identifier. For example, configuration keys for namespace my-company follow the pattern my-company:config:{configId}. This logical separation prevents data leakage between tenants while using a single Redis database.

Are namespaces created automatically?

Namespaces are created implicitly when first used, or explicitly via POST /v1/namespaces. The NamespaceService tracks registered namespaces in Redis, allowing administrators to list or delete tenants through the REST API or programmatically via deleteNamespace().

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →