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_TOKENin.env) - An Auth0 access token (end-user identity)
- A license token (X-License-Token or
- Uses CP’s
/users/whoamito 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: UUIDname: strslug: 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:
- CP admin creates an organization.
- Assigns a subscription + plan.
- 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-prodacme-demoacme-sandbox
Fields (from WorkspaceCreateRequest / WorkspaceReadResponse):
id: UUIDorganization_id: UUIDname: strslug: strtype: 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/whoamiand FE, so they’re ready for future per-workspace scoping.
2.3 User
Logical CP user, independent of the actual IdP.
Fields (simplified):
id: UUIDemail: strdisplay_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: UUIDuser_id: UUID→cp_users.idprovider: AuthProviderenum (e.g.AUTH0,AUTHENTIK,LOCAL)subject: str– stable external ID (subclaim / 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: UUIDcode: str– machine-readable (e.g.WORKSPACE_OWNER,WORKSPACE_ADMIN,WORKSPACE_MEMBER)name: str– human-friendlydescription: 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: UUIDworkspace_id: UUIDuser_id: UUIDrole_id: UUIDstatus: MembershipStatus– e.g.ACTIVE,INVITED,SUSPENDEDcreated_at,updated_at- Embedded:
user: UserReadResponserole: 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: strdescription: Optional[str]is_public: booldefault_features: Dict[str, Any]– raw JSON config for limits/features
Examples of features (already supported in schema):
alerts_enabled: true/falsemax_workspaces: intmax_dashboards_per_workspace: intmax_users_per_organization: intexports_enabled: bool, etc.
Subscription
Represents an org’s subscription to a plan.
From SubscriptionReadResponse:
id: UUIDorganization_id: UUIDplan_code: PlanCodestatus: SubscriptionStatus(e.g.active,trialing,canceled)seat_cap: intstarts_at,ends_at,trial_ends_atexternal_id: Optional[str]– ID in Stripe/Paddle/etc.
Lifecycle:
- Org is created.
- Subscription with
plan_codeis attached. - 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: UUIDorganization_id: UUIDname: strtype: 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-WestAcme On-PremAcme Demo
DPs know which deployment they are via the license token.
2.9 License
Represents an entitlement for a specific deployment.
From LicenseReadResponse:
id: UUIDsubscription_id: UUIDdeployment_id: UUIDseat_cap: intfeatures: Dict[str, Any]– effective feature setvalid_from,valid_untilkey_id: Optional[str]– which key was used to sign exportsbundle_hash: Optional[str]– hash of last exported bundlecreated_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: UUIDtoken: str– signed JWT (compact JWS)key_id: str–kidof signing key (must match public key in JWKS)algorithm: str– e.g.RS256issued_at,expires_atpayload: 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_TOKENenv var (local/dev) - Or via
X-License-Tokenheader at the gateway in production
- Either via
Inside the token:
- High-level standard claims (
iss,aud,sub,exp, etc.) - A
licenseobject 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. Auth0subclaim)
Behavior:
-
Find
AuthAccountvia(provider, subject). -
Load the
User. -
Load all
WorkspaceMemberships for that user (with embedded role). -
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:
-
Bump version in
pyproject.toml:[project] version = "0.2.0"
-
Optionally update docs:
CP_README.mdCHANGELOG.md
-
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
-
The publish workflow runs:
pytestpython -m buildtwine upload dist/*
-
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.0allows patch upgrades within0.2.*(recommended). Use==0.2.0if 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a1a10afb5764237c8486a8012942d86ccba506ff559349871e5fd9fc3a771608
|
|
| MD5 |
846b9982e8c7c22a0a88e104eee977d1
|
|
| BLAKE2b-256 |
3672015f39992be3d738c1ccbdc0bfb8018d5e1139282a639db3ddac837354d1
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ac436160455e97e164405e07fd7a641fc663dfc1ff75ab8b01cc9d64a69702dc
|
|
| MD5 |
88fc0e3262009bb0acb8b2b15d776592
|
|
| BLAKE2b-256 |
04d5a7e2833bfcf74dd1ad899fd12f4e76343737842b9d2ef3ec41891ba5fa96
|