Skip to main content

Contracts for Owndivision Control Plane ↔ Data Plane

Project description

Owndivision Control Plane – How To & Concepts

This document explains what the Control Plane (CP) currently does, what the main domain objects are (organization, workspace, user, subscription, license, etc.), and how it interacts with the Data Plane (DP).

It is written for:

  • Future you
  • Any developer onboarding to the project
  • Anyone trying to understand how CP ↔ DP ↔ Auth0 fit together

1. Big Picture

Control Plane (CP)

The CP is the “brain” of the platform:

  • Knows your customers (organizations)
  • Knows workspaces inside those organizations
  • Manages users, identities & roles
  • Manages plans, subscriptions & deployments
  • Issues license tokens (signed JWTs) that the DP uses to:
    • Validate entitlement
    • Know which deployment_id it belongs to
    • Load license features/seat caps via a LicenseContext

Data Plane (DP)

The DP is the analytics app instance:

  • Exposes /api/v1/... for chats, charts, dashboards, alerts
  • Talks to:
    • MFI DB for customer data
    • Internal DB for app metadata
  • Validates every request via:
    • A license token (X-License-Token or LICENSE_TOKEN in .env)
    • An Auth0 access token (end-user identity)
  • Uses CP’s /users/whoami to resolve:
    • Which CP user this Auth0 identity belongs to
    • Which workspaces and roles they have

2. Core Domain Objects

Below is what we currently have in the CP models & schemas.

2.1 Organization

Represents a customer organization (tenant at business level).

Key fields (simplified):

  • id: UUID
  • name: str
  • slug: str (URL-friendly identifier)
  • status: OrganizationStatus (e.g. active, suspended)
  • external_id: Optional[str] – e.g. CRM/customer id

Relationships:

  • Has many Workspaces
  • Has many Subscriptions
  • Has many Deployments (DP instances)
  • Indirectly linked to Licenses via deployments and subscriptions

Typical lifecycle:

  1. CP admin creates an organization.
  2. Assigns a subscription + plan.
  3. Creates one or more deployments (DP instances) and workspaces.

2.2 Workspace

Represents a logical environment inside an organization.

Example:
Acme Corp (org) can have workspaces:

  • acme-prod
  • acme-demo
  • acme-sandbox

Fields (from WorkspaceCreateRequest / WorkspaceReadResponse):

  • id: UUID
  • organization_id: UUID
  • name: str
  • slug: str
  • type: WorkspaceType (e.g. PROD, DEMO, TRIAL, SANDBOX)
  • is_default: bool

Usage:

  • CP uses workspaces to group users & roles.
  • DP currently does not yet segment data by workspace; it scopes by deployment_id + owner_sub. Workspaces are already exposed in /users/whoami and FE, so they’re ready for future per-workspace scoping.

2.3 User

Logical CP user, independent of the actual IdP.

Fields (simplified):

  • id: UUID
  • email: str
  • display_name: Optional[str]
  • is_active: bool

Relationships:

  • Has many AuthAccounts (concrete identities at Auth0/authentik/etc.)
  • Has many WorkspaceMemberships (with roles per workspace)

2.4 AuthAccount

Connects a User to a concrete identity provider account.

Fields:

  • id: UUID
  • user_id: UUIDcp_users.id
  • provider: AuthProvider enum (e.g. AUTH0, AUTHENTIK, LOCAL)
  • subject: str – stable external ID (sub claim / NameID)
  • idp_tenant: Optional[str] – external IdP tenant/org id

Constraint:

  • (provider, subject) is globally unique
    → ensures we can look up one CP user for a given IdP identity.

This is what /api/v1/users/whoami uses when you pass provider=auth0&subject=<sub>.


2.5 Roles

A Role describes a set of permissions in a workspace (current implementation is mostly structural; enforcement is still light).

Fields:

  • id: UUID
  • code: str – machine-readable (e.g. WORKSPACE_OWNER, WORKSPACE_ADMIN, WORKSPACE_MEMBER)
  • name: str – human-friendly
  • description: Optional[str]
  • is_system: bool – built-in vs custom

Roles are attached to memberships (below).


2.6 WorkspaceMembership

Connects a User with a Workspace and a Role.

Fields (from WorkspaceMembershipReadResponse):

  • id: UUID
  • workspace_id: UUID
  • user_id: UUID
  • role_id: UUID
  • status: MembershipStatus – e.g. ACTIVE, INVITED, SUSPENDED
  • created_at, updated_at
  • Embedded:
    • user: UserReadResponse
    • role: RoleReadResponse

Semantics:

  • This is where “who can access which workspace and with what level” lives.
  • A user can have multiple memberships across orgs & workspaces.

2.7 Plans & Subscriptions

Plan

Represents a billing / feature tier.

From PlanReadResponse:

  • code: PlanCode – stable machine code (free, pro, enterprise, …)
  • name: str
  • description: Optional[str]
  • is_public: bool
  • default_features: Dict[str, Any] – raw JSON config for limits/features

Examples of features (already supported in schema):

  • alerts_enabled: true/false
  • max_workspaces: int
  • max_dashboards_per_workspace: int
  • max_users_per_organization: int
  • exports_enabled: bool, etc.

Subscription

Represents an org’s subscription to a plan.

From SubscriptionReadResponse:

  • id: UUID
  • organization_id: UUID
  • plan_code: PlanCode
  • status: SubscriptionStatus (e.g. active, trialing, canceled)
  • seat_cap: int
  • starts_at, ends_at, trial_ends_at
  • external_id: Optional[str] – ID in Stripe/Paddle/etc.

Lifecycle:

  1. Org is created.
  2. Subscription with plan_code is attached.
  3. Licenses for deployments draw their default seat_cap / features from the subscription + plan.

2.8 Deployment

A logical DP instance (cluster/on-prem install/demo).

From DeploymentReadResponse:

  • id: UUID
  • organization_id: UUID
  • name: str
  • type: DeploymentType (CLOUD, ONPREM, DEMO, etc.)
  • region: Optional[str]
  • api_base_url: Optional[str]
  • status: DeploymentStatus

A single organization can have multiple deployments, e.g.:

  • Acme Cloud EU-West
  • Acme On-Prem
  • Acme Demo

DPs know which deployment they are via the license token.


2.9 License

Represents an entitlement for a specific deployment.

From LicenseReadResponse:

  • id: UUID
  • subscription_id: UUID
  • deployment_id: UUID
  • seat_cap: int
  • features: Dict[str, Any] – effective feature set
  • valid_from, valid_until
  • key_id: Optional[str] – which key was used to sign exports
  • bundle_hash: Optional[str] – hash of last exported bundle
  • created_at, updated_at

The key thing: this is what gets turned into a License Token (JWT) and installed in the DP.


2.10 License Bundle (License Token)

From LicenseBundleResponse:

  • license_id: UUID
  • token: str – signed JWT (compact JWS)
  • key_id: strkid of signing key (must match public key in JWKS)
  • algorithm: str – e.g. RS256
  • issued_at, expires_at
  • payload: Dict[str, Any] – decoded claims (for debugging in dev)

This token is:

  • Signed by the CP’s private key (CP_SIGNING_PRIVATE_KEY_PATH)
  • Verifiable via the CP’s JWKS endpoint
    (/api/v1/.well-known/jwks.json → used by DP)
  • Installed into the DP:
    • Either via LICENSE_TOKEN env var (local/dev)
    • Or via X-License-Token header at the gateway in production

Inside the token:

  • High-level standard claims (iss, aud, sub, exp, etc.)
  • A license object with:
    • Organization (id, name, slug, status)
    • Deployment (id, name, type, region, status)
    • Subscription (id, status, seat_cap, dates)
    • Plan (code, name, default_features)
    • Features / limits for this license

The DP reads this and builds a LicenseContext.


3. “Who Am I” Flows

3.1 On the Control Plane (/api/v1/users/whoami)

Endpoint:
GET /api/v1/users/whoami?provider=<provider>&subject=<subject>

Inputs:

  • provider – e.g. "auth0" (AuthProvider.AUTH0)
  • subject – IdP’s stable id (e.g. Auth0 sub claim)

Behavior:

  1. Find AuthAccount via (provider, subject).

  2. Load the User.

  3. Load all WorkspaceMemberships for that user (with embedded role).

  4. Return a WhoAmIResponse:

    {
      "user": {
        "id": "...",
        "email": "...",
        "display_name": "...",
        "is_active": true
      },
      "memberships": [
        {
          "workspace_id": "...",
          "workspace_name": "...",
          "workspace_slug": "...",
          "organization_id": "...",
          "status": "ACTIVE",
          "role": {
            "id": "...",
            "code": "WORKSPACE_OWNER",
            "name": "Workspace Owner",
            "description": "...",
            "is_system": true
          }
        }
      ]
    }
    

odcp-contracts: Versioning & Compatibility

The odcp-contracts package defines the public contract between the Control Plane (CP) and Data Plane (DP).

Public API

The only supported import path is:

from odcp_contracts import DeploymentLicenseClaims, LicenseBodyClaims, WhoAmIResponse

2️⃣ Versioning & release ritual (how you’ll use it)

Whenever you change public contracts:

  1. Bump version in pyproject.toml:

    [project]
    version = "0.2.0"
    
  2. Optionally update docs:

    • CP_README.md
    • CHANGELOG.md
  3. Commit and tag the release:

    git add .
    git commit -m "Bump odcp-contracts to v0.2.0"
    
    git tag -a contracts-v0.2.0 -m "odcp-contracts v0.2.0"
    git push origin main
    git push origin contracts-v0.2.0
    
  4. The publish workflow runs:

    • pytest
    • python -m build
    • twine upload dist/*
  5. In the DP repo, depend on the published package:

    # pyproject.toml of DP
    [project]
    dependencies = [
      "odcp-contracts~=0.2.0",
      # ...
    ]
    

Notes

  • odcp-contracts~=0.2.0 allows patch upgrades within 0.2.* (recommended). Use ==0.2.0 if you want fully pinned builds.
  • Stick to semantic versioning:
    • MAJOR for breaking contract changes
    • MINOR for backward-compatible additions
    • PATCH for backward-compatible fixes

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

odcp_contracts-0.1.3.tar.gz (7.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

odcp_contracts-0.1.3-py3-none-any.whl (7.6 kB view details)

Uploaded Python 3

File details

Details for the file odcp_contracts-0.1.3.tar.gz.

File metadata

  • Download URL: odcp_contracts-0.1.3.tar.gz
  • Upload date:
  • Size: 7.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for odcp_contracts-0.1.3.tar.gz
Algorithm Hash digest
SHA256 a1a10afb5764237c8486a8012942d86ccba506ff559349871e5fd9fc3a771608
MD5 846b9982e8c7c22a0a88e104eee977d1
BLAKE2b-256 3672015f39992be3d738c1ccbdc0bfb8018d5e1139282a639db3ddac837354d1

See more details on using hashes here.

File details

Details for the file odcp_contracts-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: odcp_contracts-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 7.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for odcp_contracts-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 ac436160455e97e164405e07fd7a641fc663dfc1ff75ab8b01cc9d64a69702dc
MD5 88fc0e3262009bb0acb8b2b15d776592
BLAKE2b-256 04d5a7e2833bfcf74dd1ad899fd12f4e76343737842b9d2ef3ec41891ba5fa96

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page