# How MemoryProxy Manages Multi-Tenant Routing Using spaceId in TencentDB-Agent-Memory

> Learn how MemoryProxy manages multi-tenant routing with spaceId in TencentDB-Agent-Memory. Discover how it isolates tenants, routes traffic, and scopes credentials for secure data management.

- Repository: [Tencent Cloud/TencentDB-Agent-Memory](https://github.com/TencentCloud/TencentDB-Agent-Memory)
- Tags: internals
- Published: 2026-09-01

---

**MemoryProxy isolates tenants by extracting a `spaceId` from the request URL, then uses this identifier to route traffic to dedicated backend pools, scope storage credentials, and namespace all persistence operations.**

TencentDB-Agent-Memory is a memory-enabled agent framework designed for multi-tenant cloud deployments. The MemoryProxy component enforces tenant isolation by parsing a `spaceId`—representing a unique memory instance—from every incoming request path. This identifier propagates through the entire request lifecycle, binding to per-tenant resources, storage keys, and temporary security tokens.

## Extracting spaceId from Incoming Requests

MemoryProxy implements deterministic routing by embedding the tenant identifier directly in the URL structure.

### URL Path Conventions

All agent-facing endpoints follow the pattern `/{agent}/{spaceId}/{operation}`. For example, a chat completion request targeting the `opencode` agent for tenant `abc123` would arrive as `POST /opencode/abc123/v1/chat/completions`. This placement ensures the `spaceId` is available at the edge before any backend logic executes, as documented in the repository's routing specification.

### The extractSpaceIdFromPath Implementation

In [`MemoryProxy/src/workbuddyHandler.ts`](https://github.com/TencentCloud/TencentDB-Agent-Memory/blob/main/MemoryProxy/src/workbuddyHandler.ts), the `extractSpaceIdFromPath` function parses the incoming request path using a strict regex pattern to isolate the tenant segment.

```typescript
// MemoryProxy/src/workbuddyHandler.ts#L829
function extractSpaceIdFromPath(path: string): string | undefined {
  const match = path.match(/^\/[^/]+\/([^/]+)\/.*$/);
  return match ? match[1] : undefined;
}

```

If the path lacks a `spaceId` segment, the function returns `undefined`, triggering the fallback logic described in later sections.

## Per-Tenant Backend Routing and Isolation

Once extracted, the `spaceId` drives connection pooling and downstream service binding.

### Dedicated Backend Pools

The proxy maintains separate LRU caches for each tenant to prevent cross-contamination of connections. According to [`MemoryProxy/src/types.ts`](https://github.com/TencentCloud/TencentDB-Agent-Memory/blob/main/MemoryProxy/src/types.ts), the system defines a default limit of **100 backend connections per spaceId**, configurable via the `spaceBackendLimit` parameter.

```typescript
// MemoryProxy/src/types.ts#L102
export interface ProxyConfig {
  spaceId?: string;
  spaceBackendLimit?: number; // defaults to 100
}

```

This limits resource exhaustion by ensuring one tenant cannot monopolize the proxy's connection pool.

### Context Propagation to MemoryCore

After extraction, the `spaceId` binds to the request context. At line 1118 in [`workbuddyHandler.ts`](https://github.com/TencentCloud/TencentDB-Agent-Memory/blob/main/workbuddyHandler.ts), the proxy attaches the identifier to the `metadataClient` and passes it as `serviceIdOverride` when invoking MemoryCore operations. This ensures downstream services operate within the correct tenant boundary.

```typescript
// MemoryProxy/src/workbuddyHandler.ts#L1118
const spaceId = extractSpaceIdFromPath(req.path) ?? "";
const metadataClient = getMetadataClient(config.coreSkill, spaceId, apiKey);
// Propagated to MemoryCore as serviceIdOverride

```

## Storage Layer Tenant Isolation

The `spaceId` enforces data isolation at the persistence layer through namespacing and scoped credentials.

### Namespaced Storage Keys

In [`MemoryProxy/src/storage/key-utils.ts`](https://github.com/TencentCloud/TencentDB-Agent-Memory/blob/main/MemoryProxy/src/storage/key-utils.ts), all object keys are prefixed with the tenant identifier. This prevents key collisions across tenants sharing the same underlying storage bucket.

```typescript
// MemoryProxy/src/storage/key-utils.ts#L5
const objectKey = `${bucket}/${spaceId}/${userId}/${agentSource}/${sessionId}/metadata.json`;

```

This convention applies universally to COS (Cloud Object Storage) and Redis backends, ensuring logical separation even when physical hardware is shared.

### Scoped STS Credentials

Temporary security tokens are restricted to tenant-specific paths. As defined in [`MemoryProxy/src/storage/cos-types.ts`](https://github.com/TencentCloud/TencentDB-Agent-Memory/blob/main/MemoryProxy/src/storage/cos-types.ts), STS credentials granted to the proxy are scoped to `proxy_cache/{ttl|nottl}/{spaceId}/*`, preventing tenants from accessing each other's cached data.

```typescript
// MemoryProxy/src/storage/cos-types.ts#L57
export interface STSPolicy {
  resource: `proxy_cache/${'ttl' | 'nottl'}/${string}/*`; // string = spaceId
}

```

## Skill Bridge and Downstream Integration

The skill bridge layer handles `spaceId` differently depending on the backend type. In [`MemoryProxy/src/skill/skill-bridge.ts`](https://github.com/TencentCloud/TencentDB-Agent-Memory/blob/main/MemoryProxy/src/skill/skill-bridge.ts) (lines 46-87), Redis-backed instances ignore the `spaceId` segment because they utilize a single shared instance, while COS-backed storage strictly enforces the tenant path. The `spaceId` is passed as the first argument to bridge functions, allowing adaptive routing logic.

## Administrative Cache Management

Operators can evict a specific tenant's resources without affecting others. The `evictCosSpace` function in [`MemoryProxy/src/storage/factory.ts`](https://github.com/TencentCloud/TencentDB-Agent-Memory/blob/main/MemoryProxy/src/storage/factory.ts) (lines 222-226) clears a tenant's cached backend connections and associated STS tokens.

```typescript
// MemoryProxy/src/storage/factory.ts#L222-L226
export async function evictCosSpace(spaceId: string): Promise<void> {
  await backendCache.delete(spaceId);
  await stsTokenCache.delete(spaceId);
}

```

If a request arrives without a `spaceId`, the proxy falls back to an empty string or the `_default` instance, ensuring backward compatibility while maintaining isolation for explicit tenants, as implemented in [`MemoryProxy/src/skill/kv-version-pin-repo.ts`](https://github.com/TencentCloud/TencentDB-Agent-Memory/blob/main/MemoryProxy/src/skill/kv-version-pin-repo.ts) (lines 24-29).

## Summary

- **URL Parsing**: `extractSpaceIdFromPath` extracts tenant identifiers from `/{agent}/{spaceId}/` patterns.
- **Connection Isolation**: Per-tenant LRU pools limit each `spaceId` to 100 concurrent backends.
- **Context Binding**: The `spaceId` propagates via `metadataClient` and `serviceIdOverride` to MemoryCore.
- **Storage Namespacing**: COS keys and Redis prefixes include the `spaceId` to prevent cross-tenant data leaks.
- **Credential Scoping**: STS tokens are restricted to `proxy_cache/{type}/{spaceId}/*` paths.
- **Administrative Control**: `evictCosSpace` enables targeted cache clearing per tenant.

## Frequently Asked Questions

### How does MemoryProxy extract the spaceId from a request URL?

MemoryProxy uses the `extractSpaceIdFromPath` function in [`MemoryProxy/src/workbuddyHandler.ts`](https://github.com/TencentCloud/TencentDB-Agent-Memory/blob/main/MemoryProxy/src/workbuddyHandler.ts). This utility applies the regex `/^\/[^/]+\/([^/]+)\/.*$/` to capture the segment immediately following the agent name. If the URL structure is invalid or the segment is missing, the function returns `undefined`, triggering fallback logic.

### What happens if a request does not include a spaceId?

When `extractSpaceIdFromPath` returns `undefined`, the proxy defaults to an empty string or the `_default` memory instance, as handled in [`MemoryProxy/src/skill/kv-version-pin-repo.ts`](https://github.com/TencentCloud/TencentDB-Agent-Memory/blob/main/MemoryProxy/src/skill/kv-version-pin-repo.ts). This ensures legacy clients without explicit tenant routing still function, though they operate in a shared default space rather than an isolated tenant environment.

### How does spaceId isolation work at the storage layer?

At the storage layer, `spaceId` appears in every object key path (e.g., `bucket/spaceId/userId/...`) according to [`MemoryProxy/src/storage/key-utils.ts`](https://github.com/TencentCloud/TencentDB-Agent-Memory/blob/main/MemoryProxy/src/storage/key-utils.ts). Additionally, STS temporary credentials are scoped to specific `spaceId` prefixes, preventing tenants from listing or accessing objects belonging to other memory instances, even if they share the same physical bucket.

### What is the default limit for per-tenant backend connections?

By default, each `spaceId` is limited to **100 concurrent backend connections**, defined in [`MemoryProxy/src/types.ts`](https://github.com/TencentCloud/TencentDB-Agent-Memory/blob/main/MemoryProxy/src/types.ts). This per-tenant LRU cache prevents resource monopolization and ensures fair resource distribution across all tenants sharing the proxy instance.