# How OpenSEO Manages Workspace Merging and Organization Boundaries

> Discover how OpenSEO manages workspace merging by migrating data to a central shared workspace. Learn how organization boundaries are enforced via naming, slugs, and authentication.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: internals
- Published: 2026-08-14

---

**OpenSEO handles workspace merging by detecting legacy `delegated-<user-id>` organizations and migrating all user data into a central `shared-workspace`, while enforcing organization boundaries through naming conventions, slug-based uniqueness, and authentication mode checks.**

OpenSEO structures user data around **organizations** (also called **workspaces**). In Cloudflare Access deployments, each user originally received a private delegated organization. To simplify billing and collaboration, OpenSEO introduces a shared workspace and folds legacy per-user workspaces into it. The entire migration pipeline lives in the authentication layer according to the every-app/open-seo source code.

## Legacy Workspace Detection and Counting

The merge process begins with identifying organizations that follow the legacy pattern. In [`src/server/auth/workspace-merge.ts`](https://github.com/every-app/open-seo/blob/main/src/server/auth/workspace-merge.ts), a SQL filter selects any organization whose ID matches `delegated-%`.

```ts
// Count legacy workspaces before merging
import { WorkspaceMergeService } from "@/server/auth/workspace-merge";

const { mergedWorkspaces } = await WorkspaceMergeService.countLegacyWorkspaces();
console.log(`Legacy workspaces pending merge: ${mergedWorkspaces}`);

```

The `countLegacyWorkspaces` function returns the total number of matching organizations, giving administrators visibility into the migration scope.

## The Workspace Merging Process

**`mergeLegacyWorkspaces`** performs the actual data migration, but only when the system runs in `cloudflare_access` mode. This guard prevents accidental cross-mode data movement.

The function handles several data types:

- **Projects** — including conflicting "Default" projects that get renamed to preserve unique indexes
- **Onboarding answers**
- **GSC/GA4 connections**
- **Activation timestamps** — merged by taking the earliest timestamp across all organizations

```ts
// Execute the merge (cloudflare_access mode only)
import { WorkspaceMergeService } from "@/server/auth/workspace-merge";

try {
  const result = await WorkspaceMergeService.mergeLegacyWorkspaces();
  console.log(`Merged ${result.mergedWorkspaces} legacy workspaces into shared workspace.`);
} catch (e) {
  console.error("Merge failed:", e);
}

```

After all rows are repointed to the shared workspace, the legacy organizations are permanently deleted.

## Shared Workspace Definition and Creation

The shared workspace itself is defined in [`src/server/auth/delegated-organization.ts`](https://github.com/every-app/open-seo/blob/main/src/server/auth/delegated-organization.ts) with a fixed ID of `shared-workspace`. This ID deliberately omits the `delegated-` prefix so the legacy detection filter excludes it.

```ts
// Ensure shared workspace exists on application startup
import { ensureSharedWorkspaceOrganization } from "@/server/auth/delegated-organization";

await ensureSharedWorkspaceOrganization(); // id = "shared-workspace"

```

The `ensureSharedWorkspaceOrganization` function upserts the organization record through `AuthRepository`, guaranteeing the shared workspace is available before any merge operations occur.

## How Organization Boundaries Are Enforced

OpenSEO enforces workspace boundaries through three complementary mechanisms:

### 1. Naming Conventions

- `delegated-<user-id>` — identifies legacy per-user workspaces subject to migration
- `shared-workspace` — the unified destination that persists indefinitely

### 2. Slug Generation and Uniqueness

The `ensureDelegatedOrganizationForUser` function in [`src/server/auth/delegated-organization.ts`](https://github.com/every-app/open-seo/blob/main/src/server/auth/delegated-organization.ts) constructs unique organization slugs by combining the email local-part with a hex-encoded user ID:

```ts
// Slug format: <email-local-part>-<hex-user-id>
// Example: alice-a1b2c3d4

```

This guarantees uniqueness while maintaining traceability to the originating user.

### 3. Authorization Mode Validation

The merge operation validates `process.env.AUTH_MODE === 'cloudflare_access'` before executing. Any other mode causes immediate abortion, protecting hosted deployments from unintended data reorganization.

## Key Files and Responsibilities

| File | Role |
|------|------|
| [`src/server/auth/workspace-merge.ts`](https://github.com/every-app/open-seo/blob/main/src/server/auth/workspace-merge.ts) | Core service for detecting, counting, and merging legacy organizations |
| [`src/server/auth/delegated-organization.ts`](https://github.com/every-app/open-seo/blob/main/src/server/auth/delegated-organization.ts) | Shared workspace definition, creation, and delegated organization construction |
| [`src/server/auth/repositories/AuthRepository.ts`](https://github.com/every-app/open-seo/blob/main/src/server/auth/repositories/AuthRepository.ts) | Database operations for organization records |
| [`src/server/auth/org-slug.ts`](https://github.com/every-app/open-seo/blob/main/src/server/auth/org-slug.ts) | URL-safe slug generation and hex encoding utilities |

## Summary

- **Workspace merging** in OpenSEO migrates data from `delegated-<user-id>` organizations into a single `shared-workspace`
- **Legacy detection** uses SQL pattern matching on organization IDs in [`workspace-merge.ts`](https://github.com/every-app/open-seo/blob/main/workspace-merge.ts)
- **Data integrity** is preserved through conflict resolution for projects and timestamp merging for activations
- **Boundaries** are enforced via naming conventions, unique slug generation, and strict authentication mode checks
- **The operation is irreversible** — legacy organizations are deleted after successful migration

## Frequently Asked Questions

### What happens to conflicting "Default" projects during a merge?

Conflicting "Default" projects are automatically renamed before migration. This prevents unique index violations in the database while preserving all project data from legacy workspaces.

### Can workspace merging run in hosted mode?

No. The `mergeLegacyWorkspaces` function explicitly checks for `cloudflare_access` mode and throws an error if the system is configured otherwise. This safety guard prevents data reorganization in single-tenant hosted deployments.

### How does OpenSEO ensure the shared workspace always exists?

The `ensureSharedWorkspaceOrganization` function performs an upsert operation through `AuthRepository` whenever called. Application startup routines typically invoke this to guarantee the shared workspace is available before any user operations occur.

### What activation data survives the merge?

Activation timestamps from all legacy organizations are compared, and the earliest timestamp is retained in the shared workspace. This preserves the true original activation date regardless of how many workspaces a user previously occupied.