Core Concepts
Sphere is a multi-tenant, event-driven platform built on Laravel. Before working with the API, it helps to understand the key architectural patterns that shape how data flows, how services communicate, and how tenants are isolated.
Multi-tenant architecture
Every tenant in Sphere has its own isolated PostgreSQL database. When a request arrives, the platform reads the tenant_id from the JWT token and dynamically connects to the correct database. This provides complete data isolation — one tenant can never see or modify another tenant's data.
Tenants are provisioned through sphere-auth, which orchestrates the creation process across all services. Each service creates its own tenant-specific resources (database tables, Matrix spaces, SIP extensions, storage buckets, etc.).
- Name
Tenant- Type
- concept
- Description
A top-level organizational unit. Represents a company or organization. Each tenant has its own database, users, and configuration.
- Name
Tenant slug- Type
- string
- Description
A short, human-readable identifier for the tenant (e.g.,
acme). Used in internal routing and as thetenant_short_idJWT claim.
Tenant in JWT claims
{
"tenant_id": "a7c8e9f0-1234-5678-abcd-ef0123456789",
"tenant_short_id": "acme",
"workspace_id": "c3d4e5f6-7890-1234-abcd-567890abcdef"
}
Database isolation
sphere_acme ← Tenant "Acme Corp"
sphere_beta ← Tenant "Beta Inc"
sphere_gamma ← Tenant "Gamma Ltd"
Workspaces
Within a tenant, workspaces provide organizational units for grouping users and controlling access. Each tenant has at least one default workspace created during provisioning.
Users are assigned to workspaces with a specific role that determines their permissions.
- Name
owner- Type
- workspace role
- Description
Full control over the workspace. Can manage members, settings, and all data within the workspace.
- Name
editor- Type
- workspace role
- Description
Can create, read, update, and delete data within the workspace. Cannot manage workspace settings or members.
- Name
viewer- Type
- workspace role
- Description
Read-only access to data within the workspace. Cannot create or modify records.
The active workspace is embedded in the JWT token as workspace_id. All data access is scoped to this workspace unless the user has cross-workspace permissions.
Workspace structure
{
"id": "c3d4e5f6-7890-1234-abcd-567890abcdef",
"name": "Engineering",
"slug": "engineering",
"members": [
{
"user_id": "9b1deb4d-...",
"role": "owner"
},
{
"user_id": "7c8d9e0f-...",
"role": "editor"
}
]
}
Event-driven architecture and CQRS
Sphere follows an event-driven architecture with progressive CQRS (Command Query Responsibility Segregation). This means writes and reads are handled by different models:
- Write side (Intentions): When you create or update data, you submit an Intention — a command that describes what should happen. The system validates the intention, executes it, and emits domain events.
- Read side (Projections): Domain events are processed by projectors that build optimized read models. The Timeline and Inbox are projection-based views that aggregate data from multiple sources.
This separation allows the platform to handle complex data flows (cross-service aggregation, real-time updates) without coupling the write and read paths.
Projections are eventually consistent. After submitting an intention, there may be a brief delay before the projected read models reflect the change. Workers (projector:run, search:run) process events asynchronously.
Write/Read flow
Client
│
├── POST /intentions (write)
│ └── Validate → Execute → Emit Events
│ └── Event Bus
│ ├── Timeline Projector
│ ├── Inbox Projector
│ └── Search Indexer
│
└── GET /timeline (read)
└── Query projected read model
Families and Entities
The data model in sphere-core is based on an EAV (Entity-Attribute-Value) pattern. Instead of fixed database tables for each data type, the schema is defined dynamically:
- Name
Family- Type
- concept
- Description
Defines the schema — a named collection of attributes with their types, validation rules, and display configuration. Think of it as a "class" or "template." Examples: Contact, Company, Project, Task.
- Name
Entity- Type
- concept
- Description
An instance of a family. Contains the actual data values for each attribute defined in the family. Think of it as a "record" or "row."
- Name
Attribute- Type
- concept
- Description
A field definition within a family. Has a code (machine name), label (display name), type (text, number, date, relation, etc.), and validation rules.
This model allows tenants to customize their data structure without schema migrations. New families and attributes can be created via the API at runtime.
Family definition
{
"id": "f1a2b3c4-...",
"code": "contact",
"label": "Contact",
"attributes": [
{
"code": "first_name",
"label": "First Name",
"type": "text",
"required": true
},
{
"code": "email",
"label": "Email",
"type": "email",
"required": true
},
{
"code": "company",
"label": "Company",
"type": "relation",
"relation_family": "company"
}
]
}
Entity instance
{
"id": "e5f6a7b8-...",
"family_code": "contact",
"values": {
"first_name": "Alice",
"email": "alice@acme.local",
"company": "c9d0e1f2-..."
}
}
Services architecture
Sphere is composed of multiple independent services, each responsible for a specific domain. All services sit behind a Traefik reverse proxy that handles path-based routing. Clients send all requests to a single base URL — the gateway routes them to the correct backend.
- Name
sphere-auth- Type
- /auth/*
- Description
Authorization server. Handles login, JWT issuance, token refresh, MFA, user registration, and tenant provisioning.
- Name
sphere-core- Type
- /core/*
- Description
Resource server. Manages entities, families, intentions, timeline, inbox, search, and workflows. This is the primary data API.
- Name
sphere-chat- Type
- /chat/*
- Description
Chat engine backed by Matrix (Synapse). Manages rooms, messages, and real-time communication.
- Name
sphere-voip- Type
- /voip/*
- Description
VoIP/IPBX engine backed by Asterisk. Manages SIP extensions, call routing, and WebRTC.
- Name
sphere-drive- Type
- /drive/*
- Description
File storage engine. Manages file uploads, folders, sharing, and S3-compatible storage.
- Name
sphere-activity- Type
- /activity/*
- Description
Activity and calendar management. Events, tasks, and scheduling.
- Name
sphere-notes- Type
- /notes/*
- Description
Collaborative notes with real-time editing via Yjs WebSocket.
- Name
sphere-mail- Type
- /mail/*
- Description
Email client engine (IMAP/SMTP). Manages mailbox access and sending.
- Name
sphere-usermanager- Type
- /usermanager/*
- Description
User management business app. Manages users, roles, and permissions within a tenant.
Gateway routing
Client Request
│
▼
Traefik Gateway (port 80)
│
├── /auth/* → sphere-auth (7001)
├── /core/* → sphere-core (7002)
├── /chat/* → sphere-chat (7006)
├── /voip/* → sphere-voip (7007)
├── /drive/* → sphere-drive (7008)
├── /activity/* → sphere-activity (7009)
├── /notes/* → sphere-notes (7010)
├── /mail/* → sphere-mail (7005)
└── /usermanager/* → sphere-usermanager (7004)
Hub Gateway (port 81)
│
└── /* → sphere-hub (7003)
The gateway strips the service prefix before forwarding. A request to /auth/api/v1/auth/login arrives at sphere-auth as /api/v1/auth/login.
Engine orchestration
When a new tenant or user is provisioned, sphere-auth triggers an orchestration process through sphere-core. The core service calls each engine sequentially to set up tenant-specific or user-specific resources.
The orchestration is best-effort — if one engine fails (e.g., the mail server is unreachable), the tenant is still created successfully. The failed engine is marked as not_configured and can be retried later.
Tenant provisioning
When a tenant is created, each engine receives a provisioning call to create its resources:
- chat: Creates a Matrix space and default rooms
- voip: Creates a PBX context and default extension
- drive: Creates a storage bucket with the tenant's quota
- activity: Initializes the calendar workspace
- usermanager: Sets up roles and permission structure
- notes: Initializes the collaborative workspace
- mail: Configures mailbox access (if mail server is available)
User provisioning
When a user is added to a tenant, the same engines are called to create user-specific resources (Matrix account, SIP extension, personal folders, etc.).
Provisioning flow
sphere-auth (tenant created)
│
▼
sphere-core (orchestrator)
│
├── sphere-chat ✓ Matrix space created
├── sphere-voip ✓ SIP extension 1000
├── sphere-drive ✓ 100GB bucket
├── sphere-activity ✓ Calendar initialized
├── sphere-usermanager ✓ Roles created
├── sphere-notes ✓ Workspace ready
└── sphere-mail ⚠ not_configured
Provisioning result in login response
{
"meta": {
"services": {
"core": "/core/api/v1",
"chat": "/chat/api/v1",
"voip": "/voip/api/v1",
"drive": "/drive/api/v1",
"activity": "/activity/api/v1",
"usermanager": "/usermanager/api/v1",
"notes": "/notes/api/v1",
"mail": null
}
}
}