docs: add Federation Architecture design document
Captures: - Peer-to-peer federation model (master/spoke) - Agent Federation Protocol (CONNECT, QUERY, COMMAND, EVENT, DISCONNECT) - Authentik integration for enterprise SSO and RBAC - Data sovereignty principles (query, don't replicate) - RBAC model with workspace/team hierarchy - Implementation phases targeting 0.1.0 MVP - Versioning policy (0.0.x dev, 0.1.0 MVP, 1.0.0 stable)
This commit is contained in:
@@ -70,4 +70,44 @@ When creating a new design document:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### [Federation Architecture](./federation-architecture.md)
|
||||||
|
|
||||||
|
**Status:** Design Phase
|
||||||
|
**Version:** 0.0.1
|
||||||
|
**Date:** 2025-01-29
|
||||||
|
|
||||||
|
Multi-instance federation enabling cross-organization collaboration, work/personal separation, and enterprise control with data sovereignty.
|
||||||
|
|
||||||
|
**Key Features:**
|
||||||
|
- Peer-to-peer federation (every instance can be master and/or spoke)
|
||||||
|
- Authentik integration for enterprise SSO and RBAC
|
||||||
|
- Agent Federation Protocol for cross-instance queries and commands
|
||||||
|
- Data sovereignty (query in place, never replicate)
|
||||||
|
- Single pane of glass aggregating multiple instances
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### [Multi-Tenant RLS](./multi-tenant-rls.md)
|
||||||
|
|
||||||
|
**Status:** Implemented
|
||||||
|
**Version:** 1.0
|
||||||
|
**Date:** 2025-01-29
|
||||||
|
|
||||||
|
PostgreSQL Row-Level Security for workspace isolation and defense-in-depth multi-tenancy.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
When creating a new design document:
|
||||||
|
|
||||||
|
1. Copy the structure from an existing document
|
||||||
|
2. Use ASCII diagrams for architecture (keep them simple)
|
||||||
|
3. Include code examples in TypeScript
|
||||||
|
4. Specify database schema in SQL (PostgreSQL dialect)
|
||||||
|
5. Add implementation phases with clear milestones
|
||||||
|
6. Update this README with a summary
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
**Last Updated:** 2025-01-29
|
**Last Updated:** 2025-01-29
|
||||||
|
|||||||
848
docs/design/federation-architecture.md
Normal file
848
docs/design/federation-architecture.md
Normal file
@@ -0,0 +1,848 @@
|
|||||||
|
# Federation Architecture
|
||||||
|
|
||||||
|
**Version:** 0.0.1
|
||||||
|
**Status:** Design Phase
|
||||||
|
**Author:** Mosaic Stack Team
|
||||||
|
**Date:** 2025-01-29
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [Problem Statement](#problem-statement)
|
||||||
|
2. [Architecture Overview](#architecture-overview)
|
||||||
|
3. [Identity & Authentication](#identity--authentication)
|
||||||
|
4. [Agent Federation Protocol](#agent-federation-protocol)
|
||||||
|
5. [RBAC Model](#rbac-model)
|
||||||
|
6. [Data Sovereignty](#data-sovereignty)
|
||||||
|
7. [Implementation Phases](#implementation-phases)
|
||||||
|
8. [Versioning](#versioning)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Problem Statement
|
||||||
|
|
||||||
|
### Use Cases
|
||||||
|
|
||||||
|
1. **Work/Personal Separation**
|
||||||
|
- Developer has personal projects at home and work projects at the office
|
||||||
|
- Data must stay in its respective environment
|
||||||
|
- Single pane of glass for unified visibility
|
||||||
|
|
||||||
|
2. **Enterprise Control**
|
||||||
|
- Organization runs their own Mosaic Stack instance
|
||||||
|
- Teams within the org have isolated workspaces
|
||||||
|
- IT controls access, audit, compliance
|
||||||
|
|
||||||
|
3. **Cross-Organization Collaboration**
|
||||||
|
- Partner companies need to share specific projects
|
||||||
|
- Guest access with limited permissions
|
||||||
|
- No data replication—query in place
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
|
||||||
|
- **Data Sovereignty** — Data stays where it's created, never crosses boundaries
|
||||||
|
- **Unified Control** — Single interface to view/manage multiple instances
|
||||||
|
- **Clean Severance** — Disconnect = lose access, no data to clean up
|
||||||
|
- **Enterprise Auth** — SSO, RBAC, audit trails
|
||||||
|
- **Peer-to-Peer** — Any instance can be both master and spoke
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
### Peer-to-Peer Federation Model
|
||||||
|
|
||||||
|
Every Mosaic Stack instance is a **peer** that can simultaneously act as:
|
||||||
|
- **Master** — Control and query downstream spokes
|
||||||
|
- **Spoke** — Expose capabilities to upstream masters
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────┐
|
||||||
|
│ ENTERPRISE │
|
||||||
|
│ (Corp HQ) │
|
||||||
|
└────────┬────────┘
|
||||||
|
│ speaks to
|
||||||
|
┌──────────────┼──────────────┐
|
||||||
|
▼ ▼ ▼
|
||||||
|
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||||||
|
│ IT Dept │ │ Dev Team │ │ HR Dept │
|
||||||
|
└────┬─────┘ └────┬─────┘ └──────────┘
|
||||||
|
│ │
|
||||||
|
▼ ▼
|
||||||
|
┌──────────┐ ┌──────────┐
|
||||||
|
│ Jason │◄─►│ Mike │ ← peer sharing
|
||||||
|
│ (Work) │ │ (Work) │
|
||||||
|
└────┬─────┘ └──────────┘
|
||||||
|
│
|
||||||
|
│ speaks to (personal master)
|
||||||
|
▼
|
||||||
|
┌──────────┐
|
||||||
|
│ Jason │◄─► [Future: Family instance?]
|
||||||
|
│ (Home) │◄─► [Future: Trusted collaborator?]
|
||||||
|
└──────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Component Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ IDENTITY PLANE │
|
||||||
|
│ (Authentik) │
|
||||||
|
├─────────────────────────────────────────────────────────────────┤
|
||||||
|
│ • Users (identities across instances) │
|
||||||
|
│ • Groups (teams, departments, roles) │
|
||||||
|
│ • Applications (Mosaic Stack instances) │
|
||||||
|
│ • Federation trust (OIDC/SAML between IdPs) │
|
||||||
|
└───────────────────────────┬─────────────────────────────────────┘
|
||||||
|
│ OIDC / SAML
|
||||||
|
┌───────────────────┼───────────────────┐
|
||||||
|
▼ ▼ ▼
|
||||||
|
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
|
||||||
|
│ Instance A │ │ Instance B │ │ Instance C │
|
||||||
|
│ (Home) │ │ (Work) │ │ (Partner) │
|
||||||
|
├───────────────┤ ├───────────────┤ ├───────────────┤
|
||||||
|
│ Agent Layer │ │ Agent Layer │ │ Agent Layer │
|
||||||
|
│ ┌───────────┐ │ │ ┌───────────┐ │ │ ┌───────────┐ │
|
||||||
|
│ │ Jarvis │ │ │ │ Jarvis │ │ │ │ Partner │ │
|
||||||
|
│ │ Prime │◄┼───┼─┤ Work │ │ │ │ Agent │ │
|
||||||
|
│ └───────────┘ │ │ └───────────┘ │ │ └───────────┘ │
|
||||||
|
├───────────────┤ ├───────────────┤ ├───────────────┤
|
||||||
|
│ Workspaces: │ │ Workspaces: │ │ Workspaces: │
|
||||||
|
│ • Personal │ │ • IT Ops │ │ • Shared Proj │
|
||||||
|
│ • Side Proj │ │ • Dev Team │ │ │
|
||||||
|
└───────────────┘ └───────────────┘ └───────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Identity & Authentication
|
||||||
|
|
||||||
|
### Authentik as Identity Provider
|
||||||
|
|
||||||
|
Authentik provides enterprise-grade identity management:
|
||||||
|
|
||||||
|
| Feature | Purpose |
|
||||||
|
|---------|---------|
|
||||||
|
| **OIDC/SAML** | Single sign-on across instances |
|
||||||
|
| **User Directory** | Centralized user management |
|
||||||
|
| **Groups** | Team/department organization |
|
||||||
|
| **RBAC** | Role-based access control |
|
||||||
|
| **Audit Logs** | Compliance and security tracking |
|
||||||
|
| **MFA** | Multi-factor authentication |
|
||||||
|
| **Federation** | Trust between external IdPs |
|
||||||
|
|
||||||
|
### Auth Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ Identity Provider (Authentik) │
|
||||||
|
├─────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
|
||||||
|
│ │ Users │ │ Groups │ │ Applications │ │
|
||||||
|
│ ├─────────────┤ ├─────────────┤ ├─────────────────────────┤ │
|
||||||
|
│ │ jason │ │ IT-Team │ │ mosaic-home │ │
|
||||||
|
│ │ mike │ │ Dev-Team │ │ mosaic-work-usc │ │
|
||||||
|
│ │ sarah │ │ Admins │ │ mosaic-partner-acme │ │
|
||||||
|
│ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Policies & Flows │ │
|
||||||
|
│ │ • Login flow (password + MFA) │ │
|
||||||
|
│ │ • OAuth2/OIDC scopes (profile, email, groups) │ │
|
||||||
|
│ │ • Conditional access (IP, device, time) │ │
|
||||||
|
│ │ • External IdP federation (Google, Azure AD) │ │
|
||||||
|
│ └──────────────────────────────────────────────────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
┌───────────────────┼───────────────────┐
|
||||||
|
│ OIDC │ OIDC │ OIDC
|
||||||
|
▼ ▼ ▼
|
||||||
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||||
|
│ Mosaic Home │ │ Mosaic Work │ │ Mosaic Ext │
|
||||||
|
└─────────────┘ └─────────────┘ └─────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### OIDC Token Claims
|
||||||
|
|
||||||
|
Mosaic Stack expects these claims from the IdP:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"sub": "user-uuid-123",
|
||||||
|
"preferred_username": "jason",
|
||||||
|
"email": "jason@example.com",
|
||||||
|
"groups": ["IT-Team", "Dev-Team", "Admins"],
|
||||||
|
"roles": ["workspace:ws-123:owner", "workspace:ws-456:member"],
|
||||||
|
"aud": "mosaic-home",
|
||||||
|
"iss": "https://auth.example.com"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cross-Instance Identity
|
||||||
|
|
||||||
|
When federating between instances with different IdPs:
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────┐ ┌─────────────────┐
|
||||||
|
│ Authentik A │◄── OIDC Trust ───►│ Authentik B │
|
||||||
|
│ (Home IdP) │ │ (Work IdP) │
|
||||||
|
└────────┬────────┘ └────────┬────────┘
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
▼ ▼
|
||||||
|
┌─────────────────┐ ┌─────────────────┐
|
||||||
|
│ Mosaic Home │ │ Mosaic Work │
|
||||||
|
│ │ │ │
|
||||||
|
│ User: jason │◄── Federation ───►│ User: jason │
|
||||||
|
│ (home identity)│ Protocol │ (work identity)│
|
||||||
|
└─────────────────┘ └─────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Identity Mapping:**
|
||||||
|
- Same email = same person (by convention)
|
||||||
|
- Explicit identity linking via federation protocol
|
||||||
|
- No implicit access—must be granted per instance
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Agent Federation Protocol
|
||||||
|
|
||||||
|
### Connection Lifecycle
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────┐ ┌────────────┐
|
||||||
|
│ MASTER │ │ SPOKE │
|
||||||
|
│ (Home) │ │ (Work) │
|
||||||
|
└─────┬──────┘ └─────┬──────┘
|
||||||
|
│ │
|
||||||
|
│ 1. CONNECT (auth token + scopes) │
|
||||||
|
│───────────────────────────────────────►│
|
||||||
|
│ │
|
||||||
|
│ 2. CAPABILITIES (available APIs) │
|
||||||
|
│◄───────────────────────────────────────│
|
||||||
|
│ │
|
||||||
|
│ 3. QUERY / COMMAND (ongoing) │
|
||||||
|
│◄──────────────────────────────────────►│
|
||||||
|
│ │
|
||||||
|
│ 4. EVENT (push notifications) │
|
||||||
|
│◄───────────────────────────────────────│
|
||||||
|
│ │
|
||||||
|
│ 5. DISCONNECT (clean exit) │
|
||||||
|
│───────────────────────────────────────►│
|
||||||
|
│ │
|
||||||
|
```
|
||||||
|
|
||||||
|
### Message Types
|
||||||
|
|
||||||
|
#### 1. `CONNECT` — Establish Federation Trust
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "connect",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"masterIdentity": {
|
||||||
|
"instanceId": "mosaic-home-jason",
|
||||||
|
"userId": "jason",
|
||||||
|
"instanceUrl": "https://mosaic.home.local"
|
||||||
|
},
|
||||||
|
"authToken": "oidc-access-token-or-api-key",
|
||||||
|
"requestedScopes": [
|
||||||
|
"calendar.read",
|
||||||
|
"calendar.write",
|
||||||
|
"tasks.read",
|
||||||
|
"tasks.write",
|
||||||
|
"agents.query",
|
||||||
|
"agents.command"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. `CAPABILITIES` — Spoke Advertises Available APIs
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "capabilities",
|
||||||
|
"spokeIdentity": {
|
||||||
|
"instanceId": "mosaic-work-usc",
|
||||||
|
"instanceUrl": "https://mosaic.uscllc.com"
|
||||||
|
},
|
||||||
|
"grantedScopes": ["calendar.read", "tasks.read", "tasks.write"],
|
||||||
|
"deniedScopes": ["calendar.write", "agents.command"],
|
||||||
|
"availableCommands": [
|
||||||
|
{
|
||||||
|
"name": "tasks.list",
|
||||||
|
"description": "List tasks in workspace",
|
||||||
|
"params": { "workspaceId": "string", "status": "string?" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "tasks.create",
|
||||||
|
"description": "Create a new task",
|
||||||
|
"params": { "title": "string", "description": "string?" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "calendar.today",
|
||||||
|
"description": "Get today's calendar events",
|
||||||
|
"params": {}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"availableQueries": [
|
||||||
|
{
|
||||||
|
"name": "status",
|
||||||
|
"description": "Get instance status",
|
||||||
|
"returns": "InstanceStatus"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "workspaces.list",
|
||||||
|
"description": "List accessible workspaces",
|
||||||
|
"returns": "Workspace[]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"eventSubscriptions": [
|
||||||
|
"calendar.reminder",
|
||||||
|
"tasks.assigned",
|
||||||
|
"tasks.completed"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. `QUERY` — Read-Only Data Fetch
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "query",
|
||||||
|
"id": "q-abc123",
|
||||||
|
"name": "calendar.today",
|
||||||
|
"params": { "includeAllDay": true }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "query.result",
|
||||||
|
"id": "q-abc123",
|
||||||
|
"success": true,
|
||||||
|
"data": [
|
||||||
|
{ "title": "Standup", "time": "09:00", "duration": 30 },
|
||||||
|
{ "title": "1:1 with Mike", "time": "14:00", "duration": 60 }
|
||||||
|
],
|
||||||
|
"provenance": {
|
||||||
|
"instanceId": "mosaic-work-usc",
|
||||||
|
"timestamp": "2025-01-29T14:30:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. `COMMAND` — Execute an Action
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"id": "c-def456",
|
||||||
|
"name": "tasks.create",
|
||||||
|
"params": {
|
||||||
|
"title": "Review PR #421",
|
||||||
|
"description": "Security review for auth changes",
|
||||||
|
"priority": 8,
|
||||||
|
"workspaceId": "ws-dev-team"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Sync Response:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "command.result",
|
||||||
|
"id": "c-def456",
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"taskId": "task-789",
|
||||||
|
"url": "https://mosaic.uscllc.com/tasks/task-789"
|
||||||
|
},
|
||||||
|
"provenance": {
|
||||||
|
"instanceId": "mosaic-work-usc",
|
||||||
|
"timestamp": "2025-01-29T14:31:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Async Response (for long-running):**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "command.accepted",
|
||||||
|
"id": "c-def456",
|
||||||
|
"status": "pending",
|
||||||
|
"pollUrl": "/federation/status/c-def456",
|
||||||
|
"estimatedCompletion": "30s"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5. `EVENT` — Push Notification from Spoke
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "event",
|
||||||
|
"name": "calendar.reminder",
|
||||||
|
"data": {
|
||||||
|
"title": "1:1 with Mike",
|
||||||
|
"startsIn": "15m",
|
||||||
|
"location": "Conference Room B"
|
||||||
|
},
|
||||||
|
"provenance": {
|
||||||
|
"instanceId": "mosaic-work-usc",
|
||||||
|
"timestamp": "2025-01-29T13:45:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 6. `DISCONNECT` — Clean Severance
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "disconnect",
|
||||||
|
"reason": "user-initiated",
|
||||||
|
"revokeToken": true,
|
||||||
|
"preserveHistory": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Agent-to-Agent Communication
|
||||||
|
|
||||||
|
Masters can command spokes to execute agent tasks:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"id": "c-agent-001",
|
||||||
|
"name": "agents.spawn",
|
||||||
|
"params": {
|
||||||
|
"taskTitle": "Review today's tickets",
|
||||||
|
"taskDescription": "Check GLPI for new high-priority tickets",
|
||||||
|
"inputContext": {
|
||||||
|
"priority": "high",
|
||||||
|
"category": "network"
|
||||||
|
},
|
||||||
|
"callbackUrl": "https://mosaic.home.local/federation/callback/c-agent-001"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The spoke agent executes the task and calls back with results:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "callback",
|
||||||
|
"id": "c-agent-001",
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"ticketsFound": 3,
|
||||||
|
"tickets": [
|
||||||
|
{ "id": "GLPI-1234", "title": "Switch failure in Building A" },
|
||||||
|
{ "id": "GLPI-1235", "title": "VPN connectivity issues" },
|
||||||
|
{ "id": "GLPI-1236", "title": "Printer offline 3rd floor" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## RBAC Model
|
||||||
|
|
||||||
|
### Hierarchy
|
||||||
|
|
||||||
|
```
|
||||||
|
Instance
|
||||||
|
└── Workspace (like a Teams "Team")
|
||||||
|
├── Roles: owner, admin, member, viewer, guest
|
||||||
|
├── Teams (sub-groups within workspace)
|
||||||
|
│ └── Roles: owner, admin, member
|
||||||
|
├── Projects / Channels
|
||||||
|
│ └── Permissions (inherit or override)
|
||||||
|
└── Members (users or groups from IdP)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Role Permissions Matrix
|
||||||
|
|
||||||
|
| Permission | Owner | Admin | Member | Viewer | Guest |
|
||||||
|
|------------|-------|-------|--------|--------|-------|
|
||||||
|
| View workspace | ✓ | ✓ | ✓ | ✓ | ✓* |
|
||||||
|
| Create content | ✓ | ✓ | ✓ | ✗ | ✗ |
|
||||||
|
| Edit content | ✓ | ✓ | ✓ | ✗ | ✗ |
|
||||||
|
| Delete content | ✓ | ✓ | ✗ | ✗ | ✗ |
|
||||||
|
| Manage members | ✓ | ✓ | ✗ | ✗ | ✗ |
|
||||||
|
| Manage teams | ✓ | ✓ | ✗ | ✗ | ✗ |
|
||||||
|
| Configure workspace | ✓ | ✗ | ✗ | ✗ | ✗ |
|
||||||
|
| Delete workspace | ✓ | ✗ | ✗ | ✗ | ✗ |
|
||||||
|
| Manage federation | ✓ | ✗ | ✗ | ✗ | ✗ |
|
||||||
|
|
||||||
|
*Guest: scoped to specific shared items only
|
||||||
|
|
||||||
|
### Federation RBAC
|
||||||
|
|
||||||
|
Cross-instance access is always scoped and limited:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"federation": {
|
||||||
|
"trustedInstances": [
|
||||||
|
{
|
||||||
|
"instanceId": "mosaic-work-usc",
|
||||||
|
"trustLevel": "verified",
|
||||||
|
"maxRole": "member",
|
||||||
|
"scopedWorkspaces": ["ws-shared-projects"],
|
||||||
|
"allowedCapabilities": ["tasks.read", "tasks.write", "calendar.read"],
|
||||||
|
"deniedCapabilities": ["admin.*", "federation.*"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Constraints:**
|
||||||
|
- Federated users cannot exceed `maxRole` (e.g., member can't become admin)
|
||||||
|
- Access limited to `scopedWorkspaces` only
|
||||||
|
- Capabilities are explicitly allowlisted
|
||||||
|
- Admin/federation capabilities always denied by default
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Data Sovereignty
|
||||||
|
|
||||||
|
### Core Principles
|
||||||
|
|
||||||
|
1. **Data Residency** — Data lives where it's created, never crosses boundaries
|
||||||
|
2. **Query, Don't Replicate** — Master queries spoke, doesn't store results
|
||||||
|
3. **Provenance Tagging** — Every piece of data tagged with source instance
|
||||||
|
4. **Clean Exit** — Disconnect = lose access, no cleanup needed
|
||||||
|
|
||||||
|
### Data Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────────────────────┐
|
||||||
|
│ MASTER (Home) │
|
||||||
|
│ │
|
||||||
|
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Aggregation Layer │ │
|
||||||
|
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │
|
||||||
|
│ │ │ Home Data │ │ Work Data │ │ Partner Data │ │ │
|
||||||
|
│ │ │ (stored) │ │ (queried) │ │ (queried) │ │ │
|
||||||
|
│ │ │ │ │ ⚡ live │ │ ⚡ live │ │ │
|
||||||
|
│ │ └─────────────┘ └──────┬──────┘ └──────────┬──────────┘ │ │
|
||||||
|
│ │ │ │ │ │
|
||||||
|
│ └──────────────────────────┼─────────────────────┼─────────────┘ │
|
||||||
|
│ │ │ │
|
||||||
|
└─────────────────────────────┼─────────────────────┼───────────────┘
|
||||||
|
│ │
|
||||||
|
┌─────────▼─────────┐ ┌────────▼────────┐
|
||||||
|
│ SPOKE (Work) │ │ SPOKE (Partner) │
|
||||||
|
│ Data stays here │ │ Data stays here │
|
||||||
|
└───────────────────┘ └─────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### What's Stored vs Queried
|
||||||
|
|
||||||
|
| Data Type | Home Instance | Work Instance | Notes |
|
||||||
|
|-----------|---------------|---------------|-------|
|
||||||
|
| Personal tasks | ✓ Stored | — | Only at home |
|
||||||
|
| Work tasks | Queried live | ✓ Stored | Never replicated |
|
||||||
|
| Personal calendar | ✓ Stored | — | Only at home |
|
||||||
|
| Work calendar | Queried live | ✓ Stored | Never replicated |
|
||||||
|
| Federation metadata | ✓ Stored | ✓ Stored | Connection config only |
|
||||||
|
| Query results cache | Ephemeral (5m TTL) | — | Optional, short-lived |
|
||||||
|
|
||||||
|
### Severance Procedure
|
||||||
|
|
||||||
|
When disconnecting from a spoke:
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Master initiates DISCONNECT
|
||||||
|
2. Spoke revokes master's access token
|
||||||
|
3. Spoke removes master from trusted instances
|
||||||
|
4. Master removes spoke from connection list
|
||||||
|
5. All live queries fail immediately
|
||||||
|
6. No data cleanup needed—nothing was stored
|
||||||
|
|
||||||
|
Result:
|
||||||
|
- Master retains: its own data, federation config (marked disconnected)
|
||||||
|
- Spoke retains: its own data, audit log of the connection
|
||||||
|
- Neither has: the other's data
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Phases
|
||||||
|
|
||||||
|
### Phase 1: Foundation (v0.0.x)
|
||||||
|
|
||||||
|
**Goal:** Multi-instance awareness, basic federation protocol
|
||||||
|
|
||||||
|
**Deliverables:**
|
||||||
|
- [ ] Instance identity model (instanceId, URL, public key)
|
||||||
|
- [ ] Federation connection database schema
|
||||||
|
- [ ] Basic CONNECT/DISCONNECT protocol
|
||||||
|
- [ ] Manual connection setup (no auto-discovery)
|
||||||
|
- [ ] Query/Command message handling (stub)
|
||||||
|
|
||||||
|
**Testing:**
|
||||||
|
- Two local instances can connect
|
||||||
|
- Connection persists across restarts
|
||||||
|
- Disconnect cleans up properly
|
||||||
|
|
||||||
|
### Phase 2: Authentik Integration (v0.0.x)
|
||||||
|
|
||||||
|
**Goal:** Enterprise SSO with RBAC
|
||||||
|
|
||||||
|
**Deliverables:**
|
||||||
|
- [ ] Authentik OIDC provider setup guide
|
||||||
|
- [ ] BetterAuth Authentik adapter
|
||||||
|
- [ ] Group → Role mapping
|
||||||
|
- [ ] Multi-workspace session handling
|
||||||
|
- [ ] Audit logging for auth events
|
||||||
|
|
||||||
|
**Testing:**
|
||||||
|
- Login via Authentik works
|
||||||
|
- Groups map to roles correctly
|
||||||
|
- Session isolation between workspaces
|
||||||
|
|
||||||
|
### Phase 3: Federation Protocol (v0.0.x)
|
||||||
|
|
||||||
|
**Goal:** Full query/command capability
|
||||||
|
|
||||||
|
**Deliverables:**
|
||||||
|
- [ ] QUERY message type with response streaming
|
||||||
|
- [ ] COMMAND message type with async support
|
||||||
|
- [ ] EVENT subscription and delivery
|
||||||
|
- [ ] Capability negotiation
|
||||||
|
- [ ] Error handling and retry logic
|
||||||
|
|
||||||
|
**Testing:**
|
||||||
|
- Master can query spoke calendar
|
||||||
|
- Master can create tasks on spoke
|
||||||
|
- Events push from spoke to master
|
||||||
|
- Errors handled gracefully
|
||||||
|
|
||||||
|
### Phase 4: Single Pane of Glass (v0.1.0 MVP)
|
||||||
|
|
||||||
|
**Goal:** Unified dashboard showing all instances
|
||||||
|
|
||||||
|
**Deliverables:**
|
||||||
|
- [ ] Connection manager UI
|
||||||
|
- [ ] Aggregated calendar view
|
||||||
|
- [ ] Aggregated task view
|
||||||
|
- [ ] Instance status indicators
|
||||||
|
- [ ] Visual provenance tagging (color/icon per instance)
|
||||||
|
|
||||||
|
**Testing:**
|
||||||
|
- Dashboard shows data from multiple instances
|
||||||
|
- Clear visual distinction between sources
|
||||||
|
- Offline instance shows gracefully
|
||||||
|
|
||||||
|
### Phase 5: Agent Federation (v0.1.x)
|
||||||
|
|
||||||
|
**Goal:** Cross-instance agent coordination
|
||||||
|
|
||||||
|
**Deliverables:**
|
||||||
|
- [ ] Agent spawn command via federation
|
||||||
|
- [ ] Callback mechanism for results
|
||||||
|
- [ ] Agent status querying across instances
|
||||||
|
- [ ] Cross-instance task assignment
|
||||||
|
|
||||||
|
**Testing:**
|
||||||
|
- Home agent can spawn task on work instance
|
||||||
|
- Results callback works
|
||||||
|
- Agent health visible across instances
|
||||||
|
|
||||||
|
### Phase 6: Enterprise Features (v0.2.x)
|
||||||
|
|
||||||
|
**Goal:** Production-ready for organizations
|
||||||
|
|
||||||
|
**Deliverables:**
|
||||||
|
- [ ] Admin console for federation management
|
||||||
|
- [ ] Compliance audit reports
|
||||||
|
- [ ] Rate limiting and quotas
|
||||||
|
- [ ] Webhook integration
|
||||||
|
- [ ] Multi-region support
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Versioning
|
||||||
|
|
||||||
|
### Semantic Versioning Policy
|
||||||
|
|
||||||
|
| Version | Meaning |
|
||||||
|
|---------|---------|
|
||||||
|
| `0.0.x` | Active development, breaking changes expected, internal use only |
|
||||||
|
| `0.1.0` | **MVP** — First user-testable release, core features working |
|
||||||
|
| `0.x.y` | Pre-stable iteration, API may change with notice |
|
||||||
|
| `1.0.0` | Stable release, public API contract, breaking changes require major version |
|
||||||
|
|
||||||
|
### Version Milestones
|
||||||
|
|
||||||
|
| Version | Target | Features |
|
||||||
|
|---------|--------|----------|
|
||||||
|
| 0.0.1 | Design | This document |
|
||||||
|
| 0.0.5 | Foundation | Basic federation protocol |
|
||||||
|
| 0.0.10 | Auth | Authentik integration |
|
||||||
|
| 0.1.0 | **MVP** | Single pane of glass, basic federation |
|
||||||
|
| 0.2.0 | Agents | Cross-instance agent coordination |
|
||||||
|
| 0.3.0 | Enterprise | Admin console, compliance |
|
||||||
|
| 1.0.0 | Stable | Production-ready, API frozen |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
### Transport Security
|
||||||
|
|
||||||
|
- All federation traffic over TLS 1.3
|
||||||
|
- Certificate pinning for known instances (optional)
|
||||||
|
- mTLS for high-security deployments
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
|
||||||
|
- OIDC tokens with short expiry (1h)
|
||||||
|
- Refresh tokens with longer expiry (7d)
|
||||||
|
- API keys for service-to-service (no user context)
|
||||||
|
|
||||||
|
### Authorization
|
||||||
|
|
||||||
|
- Capability-based access control
|
||||||
|
- Explicit allowlists (deny by default)
|
||||||
|
- No implicit transitivity (A→B→C doesn't mean A→C)
|
||||||
|
|
||||||
|
### Audit
|
||||||
|
|
||||||
|
- All federation connections logged
|
||||||
|
- All queries/commands logged with user identity
|
||||||
|
- Retention configurable per compliance requirements
|
||||||
|
|
||||||
|
### Secrets
|
||||||
|
|
||||||
|
- Never log tokens or credentials
|
||||||
|
- Encrypt sensitive federation config at rest
|
||||||
|
- Rotate API keys periodically
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API Endpoints
|
||||||
|
|
||||||
|
### Federation Management
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// List connected instances
|
||||||
|
GET /api/v1/federation/connections
|
||||||
|
Response: FederationConnection[]
|
||||||
|
|
||||||
|
// Initiate connection to spoke
|
||||||
|
POST /api/v1/federation/connect
|
||||||
|
{
|
||||||
|
"spokeUrl": "https://mosaic.work.example.com",
|
||||||
|
"authToken": "...",
|
||||||
|
"requestedScopes": ["calendar.read", "tasks.*"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disconnect from spoke
|
||||||
|
DELETE /api/v1/federation/connections/:instanceId
|
||||||
|
|
||||||
|
// Get connection status
|
||||||
|
GET /api/v1/federation/connections/:instanceId/status
|
||||||
|
|
||||||
|
// Update connection scopes
|
||||||
|
PATCH /api/v1/federation/connections/:instanceId
|
||||||
|
{
|
||||||
|
"requestedScopes": ["calendar.*", "tasks.*"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Federated Queries
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Query a spoke
|
||||||
|
POST /api/v1/federation/query
|
||||||
|
{
|
||||||
|
"instanceId": "mosaic-work-usc",
|
||||||
|
"query": "calendar.today",
|
||||||
|
"params": { "includeAllDay": true }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute command on spoke
|
||||||
|
POST /api/v1/federation/command
|
||||||
|
{
|
||||||
|
"instanceId": "mosaic-work-usc",
|
||||||
|
"command": "tasks.create",
|
||||||
|
"params": { "title": "Review PR", "priority": 8 }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Spoke Configuration (for instances accepting connections)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Get spoke settings
|
||||||
|
GET /api/v1/federation/spoke/settings
|
||||||
|
|
||||||
|
// Update spoke settings
|
||||||
|
PATCH /api/v1/federation/spoke/settings
|
||||||
|
{
|
||||||
|
"acceptConnections": true,
|
||||||
|
"allowedMasters": ["mosaic-home-jason"],
|
||||||
|
"defaultScopes": ["calendar.read", "tasks.read"],
|
||||||
|
"maxScopes": ["calendar.*", "tasks.*"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// List connected masters
|
||||||
|
GET /api/v1/federation/spoke/masters
|
||||||
|
|
||||||
|
// Revoke master access
|
||||||
|
DELETE /api/v1/federation/spoke/masters/:instanceId
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Glossary
|
||||||
|
|
||||||
|
| Term | Definition |
|
||||||
|
|------|------------|
|
||||||
|
| **Instance** | A single Mosaic Stack deployment |
|
||||||
|
| **Master** | Instance that initiates connection and queries spoke |
|
||||||
|
| **Spoke** | Instance that accepts connections and serves data |
|
||||||
|
| **Peer** | An instance that can be both master and spoke |
|
||||||
|
| **Federation** | Network of connected Mosaic Stack instances |
|
||||||
|
| **Scope** | Permission to perform specific actions (e.g., `calendar.read`) |
|
||||||
|
| **Capability** | API endpoint exposed by a spoke |
|
||||||
|
| **Provenance** | Source attribution for data (which instance it came from) |
|
||||||
|
| **Severance** | Clean disconnection with no data cleanup required |
|
||||||
|
| **IdP** | Identity Provider (e.g., Authentik) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [Authentik Documentation](https://goauthentik.io/docs/)
|
||||||
|
- [OIDC Specification](https://openid.net/specs/openid-connect-core-1_0.html)
|
||||||
|
- [Multi-Tenant RLS Design](./multi-tenant-rls.md)
|
||||||
|
- [Agent Orchestration Design](./agent-orchestration.md)
|
||||||
|
- [Architecture Decisions](./architecture-decisions.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
|
||||||
|
1. **Identity Linking:** How do we link the same person across instances with different IdPs?
|
||||||
|
2. **Conflict Resolution:** What happens if the same entity is modified on multiple instances?
|
||||||
|
3. **Offline Handling:** How long to cache spoke data when offline?
|
||||||
|
4. **Rate Limiting:** How to prevent spoke overload from aggressive masters?
|
||||||
|
5. **Discovery:** Should instances be discoverable, or always manual connection?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Next Steps:**
|
||||||
|
1. Review and approve this design document
|
||||||
|
2. Create GitHub issues for Phase 1 tasks
|
||||||
|
3. Set up Authentik development instance
|
||||||
|
4. Begin federation protocol implementation
|
||||||
|
|
||||||
|
**Questions/Feedback:** Open an issue in `mosaic-stack` repo with label `federation`.
|
||||||
Reference in New Issue
Block a user