> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kair.is/llms.txt
> Use this file to discover all available pages before exploring further.

# Workspace Join Flow

> Sequence diagram: invitation, acceptance, and workspace membership.

# Workspace Join and Session Participation Flow

This document describes the complete process of joining a workspace and accessing sessions, covering both guest and authenticated user flows.

## Overview

There are two primary methods for joining a workspace:

1. **Guest Join**: Users can join as guests by providing their name and email. They receive a magic link via email to authenticate and access the workspace.
2. **Authenticated User Join**: Existing users can log in with their credentials to join a workspace directly.

Both methods require a valid access code that is generated using HMAC-SHA256 and rotates hourly.

## Guest Join Flow

The guest join flow allows users to access a workspace without creating a full account. They provide basic information and receive a time-limited magic link via email.

```mermaid theme={null}
sequenceDiagram
    participant User
    participant Frontend
    participant API
    participant DB
    participant Email

    User->>Frontend: Navigate to /workspaces/:id/join?code=...
    Frontend->>API: GET /workspaces/:id/public-info?code=...
    API->>API: Validate access code (HMAC)
    API->>DB: Fetch workspace
    DB-->>API: Workspace details
    API-->>Frontend: Workspace name and ID

    Frontend->>User: Display join form
    User->>Frontend: Submit name, email, privacy acceptance
    Frontend->>API: POST /workspaces/:id/join
    API->>DB: Begin transaction
    API->>DB: Check for existing guest user
    alt User exists
        API->>DB: Use existing user
    else New user
        API->>DB: Create guest user (is_guest=true)
    end
    API->>DB: Add user to workspace (user_workspace junction)
    API->>DB: Delete old guest access token if exists
    API->>DB: Create new guest_access_token (8 hour expiry)
    API->>DB: Commit transaction
    API->>Email: Send magic link email
    API-->>Frontend: Success response
    Frontend->>User: Show "Check your email" message

    User->>Email: Click magic link
    Email->>Frontend: Navigate to /join?token=...
    Frontend->>API: POST /auth/validate-magic-link
    API->>DB: Find token, check expiry and usage
    alt Token valid
        API->>DB: Mark token as accessed
        API->>API: Generate JWT token (8 hour expiry)
        API-->>Frontend: JWT token + user info + workspace_id
        Frontend->>Frontend: Store auth token
        Frontend->>User: Redirect to /sessions?workspaceId=...
    else Token invalid/expired
        API-->>Frontend: Error response
        Frontend->>User: Show error message
    end
```

## Authenticated User Join Flow

Existing users can join a workspace by logging in with their credentials. This provides immediate access without email verification.

```mermaid theme={null}
sequenceDiagram
    participant User
    participant Frontend
    participant API
    participant DB

    User->>Frontend: Navigate to /workspaces/:id/join?code=...
    Frontend->>API: GET /workspaces/:id/public-info?code=...
    API->>API: Validate access code (HMAC)
    API->>DB: Fetch workspace
    DB-->>API: Workspace details
    API-->>Frontend: Workspace name and ID

    Frontend->>User: Display login form
    User->>Frontend: Submit email and password
    Frontend->>API: POST /api/auth/login
    API->>DB: Verify credentials
    alt Credentials valid
        API->>API: Generate JWT token
        API-->>Frontend: JWT token + user info
        Frontend->>Frontend: Store auth token
        Frontend->>User: Redirect to /sessions?workspaceId=...
    else Credentials invalid
        API-->>Frontend: Error response
        Frontend->>User: Show error message
    end
```

## Magic Link Validation Flow

When a guest user clicks the magic link in their email, the token is validated and exchanged for a JWT token.

```mermaid theme={null}
sequenceDiagram
    participant User
    participant Frontend
    participant API
    participant DB

    User->>Frontend: Click magic link (/join?token=...)
    Frontend->>API: POST /auth/validate-magic-link
    Note over API: Validate token
    API->>DB: Find guest_access_token by token
    DB-->>API: Token record

    alt Token not found
        API-->>Frontend: 401 Unauthorized
        Frontend->>User: Show "Invalid link" error
    else Token expired
        API-->>Frontend: 401 Unauthorized
        Frontend->>User: Show "Link expired" error
    else Token already used
        API-->>Frontend: 401 Unauthorized
        Frontend->>User: Show "Link already used" error
    else Token valid
        API->>DB: Update token (set accessed_at)
        API->>DB: Fetch user details
        API->>API: Create guest JWT (8 hour expiry)
        API-->>Frontend: JWT token + user + workspace_id
        Frontend->>Frontend: Store auth token in localStorage
        Frontend->>User: Redirect to /sessions?workspaceId=...
    end
```

## Session Access

After joining a workspace (via either method), users are redirected to the sessions page. Sessions are filtered based on workspace membership.

### Workspace Membership

Users gain access to workspace sessions through the `user_workspace` junction table:

* **Guest users**: Added to `user_workspace` when they join via the guest flow
* **Regular users**: Can be added to workspaces by workspace owners or admins
* **Workspace owners**: Automatically have access to their own workspaces

### Session Filtering

When listing sessions (`GET /sessions`), the API:

1. Verifies the user's JWT token
2. Retrieves workspace IDs from `user_workspace` where the user is a member
3. Also includes workspaces where the user is the owner (`workspace.owner_user_id`)
4. Filters sessions to only those belonging to accessible workspaces

## Data Model

### Users

* **Regular users**: `is_guest = false`, have password hashes, can log in normally
* **Guest users**: `is_guest = true`, no password hash, authenticate via magic links
* Both types can be members of workspaces via the junction table

### Workspaces

* Each workspace has an `owner_user_id` (nullable)
* Workspaces have a `workspace_type` (Private or Shared)
* Workspace information is accessible via access codes

### User-Workspace Junction Table

The `user_workspace` table links users to workspaces:

* `user_id`: Foreign key to users
* `workspace_id`: Foreign key to workspaces
* `created_at`, `updated_at`: Timestamps

### Guest Access Tokens

The `guest_access_token` table stores magic link tokens:

* `token`: Unique token string
* `user_id`: Associated guest user
* `workspace_id`: Target workspace
* `expires_at`: Expiration timestamp (8 hours from creation)
* `accessed_at`: Timestamp when token was used (NULL if unused)
* Tokens are one-time use (checked via `accessed_at`)

### Sessions

* Sessions belong to a workspace (`workspace_id`)
* Users can only access sessions in workspaces they're members of
* Session access is determined by workspace membership, not direct user-session relationships

## API Endpoints

### GET `/workspaces/:id/public-info?code=...`

Validates an access code and returns public workspace information.

**Query Parameters:**

* `code`: HMAC-based access code (valid for current or previous hour)

**Response:**

```json theme={null}
{
	"id": "workspace-uuid",
	"name": "Workspace Name"
}
```

**Errors:**

* `400`: Invalid workspace ID
* `403`: Invalid or expired access code
* `404`: Workspace not found

### POST `/workspaces/:id/join`

Creates or finds a guest user and adds them to a workspace. Generates a magic link token and sends it via email.

**Request Body:**

```json theme={null}
{
	"name": "User Name",
	"email": "user@example.com",
	"privacy_accepted": true
}
```

**Response:**

```json theme={null}
{
	"success": true,
	"message": "Magic link sent to user@example.com. Check your email to access the workspace.",
	"user_id": "user-uuid"
}
```

**Errors:**

* `400`: Missing required fields, invalid email, privacy not accepted
* `404`: Workspace not found

### POST `/auth/validate-magic-link`

Validates a magic link token and returns a JWT token for authentication.

**Request Body:**

```json theme={null}
{
	"token": "magic-link-token-string"
}
```

**Response:**

```json theme={null}
{
	"token": "jwt-token-string",
	"user": {
		"id": "user-uuid",
		"email": "user@example.com",
		"name": "User Name",
		"is_guest": true
	},
	"workspace_id": "workspace-uuid"
}
```

**Errors:**

* `400`: Token is required
* `401`: Invalid, expired, or already used token

### POST `/auth/magic-link`

Requests a new magic link for an existing guest user.

**Request Body:**

```json theme={null}
{
	"email": "user@example.com",
	"workspace_id": "workspace-uuid"
}
```

**Response:**

```json theme={null}
{
	"success": true,
	"message": "Magic link sent to user@example.com. Check your email."
}
```

**Errors:**

* `400`: Invalid workspace ID
* `404`: Workspace or guest account not found

### GET `/workspaces/:id/join-url`

Generates a join URL with access code for a workspace. Requires admin authentication.

**Response:**

```json theme={null}
{
	"join_url": "/workspaces/{id}/join?code={access_code}",
	"access_code": "hmac-generated-code",
	"expires_in_hours": 1
}
```

## Key Points

### Access Code Security

* **HMAC-based**: Access codes are generated using HMAC-SHA256 with the workspace ID and hourly timestamp
* **Hourly rotation**: Codes are valid for the current hour and the previous hour (grace period)
* **Secret key**: Uses the JWT secret from configuration
* **Format**: `HMAC-SHA256(workspace_id:timestamp_hour, secret)` encoded as hex

### Magic Link Security

* **Expiration**: Magic links expire 8 hours after creation
* **One-time use**: Tokens are marked as accessed when used (`accessed_at` is set)
* **Token generation**: Uses cryptographically secure random token generation
* **Email delivery**: Magic links are sent via email with workspace context

### Guest User Lifecycle

1. **Creation**: Guest users are created when they first join a workspace
2. **Reuse**: If a guest user with the same email exists, they are reused (not duplicated)
3. **Workspace membership**: Guest users are added to the `user_workspace` junction table
4. **Token management**: Old tokens for the same user-workspace pair are deleted when new ones are created
5. **Disabling**: Guest users can be disabled (but not deleted) when removed from workspaces

### Session Access Control

* Sessions are filtered by workspace membership
* Users can only see sessions in workspaces they're members of
* Workspace owners automatically have access to their workspaces
* Admins can access all workspaces and sessions

## References

* Backend routes: `crates/api/src/routes/guest_routes.rs`, `crates/api/src/routes/workspace_routes.rs`
* Frontend pages: `web/src/routes/join/+page.svelte`, `web/src/routes/workspaces/[workspaceId]/join/+page.svelte`
* API utilities: `web/src/lib/api/auth.ts`, `web/src/lib/api/workspace.ts`
