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 the tenant_short_id JWT 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.

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)

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
    }
  }
}

Was this page helpful?