Teams & Scopes

Core Concept Phase 1+ 5 min read

xbrain's entire data isolation model is built on teams. Every piece of data — conversations, extracted facts, Drive files, agent outputs — belongs to exactly one team and is permanently tagged with that team's scope identifier. This is not an optional feature: it is enforced at every layer of the stack.

Team Isolation

In xbrain, every piece of data belongs to exactly one team. The X-Team-Scope HTTP header is mandatory on every authenticated API call. Team A cannot read, write, or search Team B's data — enforced at the API layer, not just the UI.

This isolation is absolute. Even if two users share the same Google account or the same LibreChat instance, their data remains separated if they belong to different teams. There is no cross-team query, no cross-team aggregation, and no way to bypass the scope check via the API.

Design Principle

Security by isolation, not by obscurity. The X-Team-Scope check is enforced server-side on every write and every search. Removing it from the UI would not grant access to another team's data.

The team_scope Field

Every memory item carries a team_scope field. If the item's team_scope doesn't match the X-Team-Scope header sent with the request, the write returns HTTP 400. This prevents data from being written to the wrong team's namespace, even by accident.

bash# This works — team_scope matches header
curl -X POST https://api.grooveos.app/v1/memory/upsert \
  -H "X-Team-Scope: excalibur" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "item": {
      "team_scope": "excalibur",
      "content": "Q2 planning is confirmed for May 15th",
      "truth_level": "WORKING",
      "source": "librechat:conv_abc123"
    }
  }'

# This fails — team_scope mismatch
curl -X POST https://api.grooveos.app/v1/memory/upsert \
  -H "X-Team-Scope: excalibur" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "item": {
      "team_scope": "other-team",
      "content": "..."
    }
  }'
# → 400 Bad Request: item.team_scope must match X-Team-Scope header

The X-Team-Scope header is validated against the authenticated user's team memberships. A user cannot set an arbitrary team scope — the value must correspond to a team they are a member of.

Project Scopes

Within a team, data can be further scoped by project (project_scope). This allows isolation of e.g. fundraising data from engineering data within the same team. Project scopes are optional — if omitted, a memory item is visible to all team members.

The full tagging contract requires at minimum: team_scope, truth_level, source, and visibility. The project_scope and confidence fields are strongly recommended.

Visibility Options

Visibility Who Can See It Typical Use Case
team All authenticated members of the team Shared facts, architecture decisions, team agreements
project Only members with access to that project_scope Fundraising docs (separate from engineering data), client-specific data
private Only the owner (source_user_id) Personal notes, draft thoughts not yet shared with the team
json — memory item with full tagging contract{
  "team_scope": "excalibur",
  "project_scope": "fundraising",
  "visibility": "project",
  "truth_level": "VALIDATED",
  "confidence": 0.95,
  "source": "librechat:conv_def456",
  "source_user_id": "google:108765432109876543",
  "validation_status": "peer_reviewed",
  "content": "Series A target: $3M at $15M pre-money valuation"
}

User Management

Users authenticate via Google OAuth (SSO) — the same identity works across LibreChat and Open WebUI. xbrain uses the OIDC claims from Google to build a stable user identity.

Single Identity Across Frontends

Because both LibreChat and Open WebUI use Google OAuth with the same GOOGLE_CLIENT_ID, a user's identity is consistent across all xbrain frontends. The source_user_id is always derived from the same Google sub claim.

Creating Teams

Teams are created by an admin via the memory-api admin endpoints. Once created, a team has a team_scope string identifier that is used in all API calls and memory items. The identifier should be short, lowercase, and URL-safe (e.g. excalibur, engineering, growth).

bash — create a new teamcurl -X POST https://api.grooveos.app/v1/admin/teams \
  -H "Authorization: Bearer $ADMIN_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Engineering Team",
    "scope": "engineering"
  }'

# Response:
# {
#   "team_id": "team_abc123",
#   "scope": "engineering",
#   "name": "Engineering Team",
#   "created_at": "2026-05-06T10:00:00Z"
# }

After creating the team, add users to it via the team membership endpoint:

bash — add a user to a teamcurl -X POST https://api.grooveos.app/v1/admin/teams/engineering/members \
  -H "Authorization: Bearer $ADMIN_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "google:108765432109876543",
    "role": "member"
  }'

Drive Folder Mapping per Team

Each team can have one or more Google Drive folders mapped to it. When a file is updated in a mapped folder, it's automatically synced to xbrain and tagged with that team's scope. Drive mappings tie a specific Google Drive folder to a team_scope and optionally a project_scope.

bash — map a Drive folder to a teamcurl -X POST https://api.grooveos.app/v1/admin/drive/mappings \
  -H "Authorization: Bearer $ADMIN_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "team_scope": "excalibur",
    "project_scope": "fundraising",
    "folder_id": "1ABC...xyz",
    "folder_name": "Fundraising Docs"
  }'

# After this mapping, any file added or updated in the Drive folder
# is automatically picked up by drive-sync and stored in memory-api
# with team_scope="excalibur" and project_scope="fundraising".

Multiple Folders per Team

Multiple Drive folders can be mapped to the same team with different project_scope values. For example, the excalibur team might have a fundraising folder and an engineering folder, each synced with their respective project scopes. See the Drive Sync documentation for details on webhook setup and sync frequency.

Drive Mapping Properties

Field Required Description
team_scope Yes The team that owns the synced content
project_scope No Optional project subdivision within the team
folder_id Yes Google Drive folder ID (from the URL)
folder_name No Human-readable name for the mapping (display only)
watch_enabled No Whether to set up a Drive push notification webhook (default: true)