Skip to main content

Spaces

Organize memories into isolated workspaces with granular access control.

What are Spaces?

Spaces are isolated workspaces within your account that provide logical separation for memories. They enable multi-tenant applications, project organization, and fine-grained access control.

Use Cases
  • Separate memories per user in multi-tenant apps
  • Organize by project, team, or domain
  • Implement access control with scoped API keys
  • Isolate development/staging/production data
import { Trix } from '@trixdb/client';

const trix = new Trix({ apiKey: process.env.TRIX_API_KEY });

// Create a space for a specific user
const space = await trix.spaces.create({
  name: 'user_alice',
  description: 'Personal memories for Alice'
});

console.log(space.id);   // spc_abc123...
console.log(space.name); // user_alice
console.log(space.slug); // user-alice

Space Slugs

Every space has a slug - a human-readable, URL-safe identifier that makes it easy to reference spaces in API calls, CLI commands, and URLs. Slugs are automatically generated from the space name, or you can provide a custom one.

Why Use Slugs?
  • Human-readable and easy to remember
  • URL-safe (lowercase, hyphens, no special characters)
  • Works in API endpoints and CLI commands
  • Old slugs automatically redirect when renamed

Auto-Generated Slugs

When you create a space, a slug is automatically generated from the name by converting to lowercase and replacing spaces with hyphens:

// Create with auto-generated slug
const space = await trix.spaces.create({
  name: 'My Research Project',
  description: 'ML experiments'
});

console.log(space.id);   // spc_abc123...
console.log(space.name); // My Research Project
console.log(space.slug); // my-research-project

// Access by slug (easier to remember!)
const space = await trix.spaces.get('my-research-project');

Custom Slugs

You can provide a custom slug when creating a space. This is useful when you want a shorter or more specific identifier:

// Create with custom slug
const space = await trix.spaces.create({
  name: 'Work Projects',
  slug: 'work',  // custom slug
  description: 'Work stuff'
});

console.log(space.name); // Work Projects
console.log(space.slug); // work

// Access using the custom slug
const space = await trix.spaces.get('work');

Updating Slugs

When you update a space's slug, the old slug continues to work via automatic redirect. This ensures existing integrations and bookmarks don't break:

// Update slug (old slug still works via redirect)
await trix.spaces.update('my-research-project', {
  slug: 'ml-research'
});

// Both of these now work:
const space1 = await trix.spaces.get('ml-research');      // new slug
const space2 = await trix.spaces.get('my-research-project'); // old slug redirects

Slug redirects are permanent - old slugs will always redirect to the current slug. This means you can safely rename spaces without breaking existing integrations.

Using Spaces with Memories

When creating or searching memories, specify the space to work within:

// Create memories in different spaces
await trix.memories.create({
  content: 'Alice prefers coffee in the morning',
  spaceId: 'user_alice'  // Can use space name or ID
});

await trix.memories.create({
  content: 'Bob prefers tea in the afternoon',
  spaceId: 'user_bob'
});

// Search only returns memories from the specified space
const aliceResults = await trix.memories.list({
  q: 'morning beverages',
  spaceId: 'user_alice'
});
// Returns: "Alice prefers coffee in the morning"

const bobResults = await trix.memories.list({
  q: 'morning beverages',
  spaceId: 'user_bob'
});
// Returns nothing - Bob's memory is about afternoon

Memories are strictly isolated between spaces. A search in one space will never return memories from another space.

Default Space

Memories created without a space go into the default space, which is created automatically for every account:

// These are equivalent - both use the default space
await trix.memories.create({
  content: 'Shared knowledge'
});

await trix.memories.create({
  content: 'Also shared',
  spaceId: 'default'
});

// Search the default space
const results = await trix.memories.list({
  q: 'knowledge'
  // No space specified = searches default space
});

Access Control

Control who can access each space using scoped API keys. When creating an API key, you can restrict it to specific spaces with specific permissions:

PermissionDescriptionAllowed Operations
readRead-only accessList, search, get memories
read_writeRead and write accessAll read operations + create, update memories
adminFull accessAll operations including delete and space management

Creating Scoped API Keys

// Create scoped API keys via the REST API
// POST /v1/api-keys
const response = await fetch('https://api.trixdb.com/v1/api-keys', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.TRIX_API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'alice-key',
    spaces: ['user_alice'],
    permission: 'read_write'
  })
});
const aliceKey = await response.json();

API keys with admin permission have access to all spaces in the account. Use scoped keys in production for better security.

Managing Spaces

List Spaces

// List all spaces in your account
const { data: spaces } = await trix.spaces.list();

spaces.forEach(space => {
  console.log(`${space.name} (${space.slug}): ${space.memory_count} memories`);
});

Get Space Details

// Get details about a specific space (by slug or ID)
const space = await trix.spaces.get('user-alice');

console.log({
  id: space.id,
  name: space.name,
  slug: space.slug,
  description: space.description,
  memoryCount: space.memory_count,
  createdAt: space.created_at
});

Update Space

// Update space name, slug, or description
await trix.spaces.update('user-alice', {
  name: 'user_alice_v2',
  slug: 'alice',  // Change slug (old slug still redirects)
  description: 'Updated description for Alice'
});

Delete Space

// Delete a space - WARNING: This deletes all memories in the space!
await trix.spaces.delete('old_project');

// To move memories first, list and re-create in the new space
const old = await trix.memories.list({ spaceId: 'old_project' });
for (const mem of old.data) {
  await trix.memories.create({ content: mem.content, spaceId: 'archive', tags: mem.tags });
  await trix.memories.delete(mem.id);
}
await trix.spaces.delete('old_project');

Deleting a space permanently deletes all memories in it. This cannot be undone. Consider exporting first, or merge the space into another to keep its memories.

Space Naming

Space names must be unique within your account and follow these rules:

  • 2-64 characters long
  • Lowercase letters, numbers, underscores, and hyphens only
  • Must start with a letter
  • Cannot be default, none, or all (reserved)

Naming Conventions

// Good naming patterns
'user_123'           // User ID
'project_alpha'      // Project name
'team_engineering'   // Team name
'customer_acme'      // Customer/tenant
'dev', 'staging', 'prod'  // Environment

// Can also use hyphens
'user-123'
'project-alpha'

Slug Format

Slugs are auto-generated from names by converting to lowercase and replacing spaces and special characters with hyphens. You can also provide custom slugs:

// Auto-generated slugs from names
'My Research Project'  -> 'my-research-project'
'User Alice'           -> 'user-alice'
'Project_Alpha'        -> 'project-alpha'

// Custom slug example
const space = await trix.spaces.create({
  name: 'Q4 2024 Planning',
  slug: 'q4-planning',  // shorter custom slug
  description: 'Planning documents'
});

Multi-Tenant Patterns

Spaces are ideal for multi-tenant applications where each user or organization needs isolated memories:

// Pattern 1: Space per user
async function getUserSpace(userId: string) {
  const spaceName = `user_${userId}`;

  // Create space if it doesn't exist
  try {
    return await trix.spaces.get(spaceName);
  } catch (e) {
    return await trix.spaces.create({
      name: spaceName,
      description: `Personal space for user ${userId}`
    });
  }
}

// Pattern 2: Space per organization with user namespacing
async function storeForUser(orgId: string, userId: string, content: string) {
  await trix.memories.create({
    content,
    spaceId: `org_${orgId}`,  // One space per org
    metadata: { userId }     // Track user in metadata
  });
}

// Pattern 3: Shared + personal spaces
async function search(userId: string, query: string) {
  // Search user's personal space
  const personal = await trix.memories.list({
    q: query,
    spaceId: `user_${userId}`
  });

  // Also search shared knowledge base
  const shared = await trix.memories.list({
    q: query,
    spaceId: 'shared_knowledge'
  });

  return [...personal.data, ...shared.data];
}

Best Practices

  • Use meaningful names: Space names should clearly indicate their purpose (e.g., user_123, project_alpha).
  • Create spaces upfront: Create spaces when users/projects are created, not lazily when the first memory is stored.
  • Use scoped API keys: In production, use API keys scoped to specific spaces rather than admin keys.
  • Monitor space sizes: Track memory counts per space to understand usage patterns and optimize storage.
  • Consider cross-space search: If you need to search across multiple spaces, consider a "shared" space for common knowledge.