Metadata-Version: 2.4
Name: odcp-contracts
Version: 2.4.0
Summary: Contracts for Owndivision Control Plane ↔ Data Plane
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: pydantic[email]<3.0,>=2.7
Requires-Dist: pyjwt<3.0.0,>=2.10.1

# Owndivision Control Plane: Concepts And How It Fits With DP

This document explains what the Control Plane (CP) does today, which domain objects it owns, and how it interacts with the Data Plane (DP) and Auth0.

Use this as the conceptual companion to the operator manuals in `docs/howtos/`.

## 1. Big Picture

### Control Plane

CP is the administration and policy system. It owns the business/control facts:

- Customer organizations.
- Workspaces inside organizations.
- Users, Auth0 identities, invitations, memberships, groups, roles, and permissions.
- Security settings such as SSO, sessions, password policy, IP allowlist, and workspace retention settings.
- Branding snapshots.
- Notification Centre configuration and delivery history.
- Notification channel configuration.
- Data-source registry metadata and source-specific permission codes.
- Billing plans, subscriptions, deployments, and signed license JWTs.
- Audit logs for administrative actions.

CP does not run customer analytics queries, chats, dashboards, sync jobs, or live connector execution. Those belong to DP.

### Data Plane

DP is the runtime analytics product. It owns runtime/customer-data behavior:

- Data-source credentials and OAuth tokens.
- Data sync jobs and connector health.
- Query execution.
- Chats, charts, dashboards, alerts, and customer-facing analytics flows.
- Runtime tenant isolation and per-request authorization enforcement.

DP consumes CP facts. It should not guess policy on its own.

### Auth0

Auth0 is the identity provider. It proves who the human user is.

CP maps Auth0 identity to product meaning:

- Which CP user row corresponds to the Auth0 subject.
- Which organizations and workspaces that user can access.
- Which role, permissions, groups, and RBAC revision apply.
- Whether the user is a superadmin.

## 2. The Runtime Contract

DP should enforce access by combining three sources of truth:

```text
1. License JWT from CP
   "Is this DP deployment allowed to run for this customer/deployment?"

2. CP whoami response
   "Who is this user, and what are they allowed to do in this workspace?"

3. DP connector/runtime state
   "Can DP actually connect to and query this source safely?"
```

For example, a data-source query should be allowed only when:

- The deployment license is valid.
- The request user is active.
- The user has an active membership in the selected workspace.
- The user has `data_sources.<slug>.query`.
- DP has a configured connector for that workspace and source slug.
- The query is scoped to the licensed organization/workspace/deployment.

## 3. Core Domain Objects

### Organization

An organization is the business tenant, for example `Acme Corp`.

Typical fields:

- `id`
- `name`
- `slug`
- `status`
- `external_id`
- Auth0 organization metadata when configured

Relationships:

- Has many workspaces.
- Has subscriptions.
- Has deployments.
- Has organization-level security and SSO configuration.

Typical lifecycle:

1. Superadmin creates the organization.
2. Superadmin creates one or more workspaces.
3. Superadmin sets up billing/subscription/deployment/license.
4. Admins configure branding, security, users, roles, and data-source registry.

### Workspace

A workspace is an environment inside an organization.

Examples:

- `Acme Production`
- `Acme Demo`
- `Acme Sandbox`

Typical fields:

- `id`
- `organization_id`
- `name`
- `slug`
- `type`: `prod`, `demo`, `trial`, or `sandbox`
- `is_default`

CP uses workspaces to scope memberships, groups, data sources, branding, settings, and many admin UI operations. DP should treat `workspace_id` as part of runtime tenant isolation.

### User

A user is the CP-side person record.

Typical fields:

- `id`
- `email`
- `display_name`
- `is_active`
- `is_superadmin`
- `auth0_user_id`

A user can belong to multiple workspaces across organizations.

### AuthAccount

An AuthAccount links a CP user to an identity-provider account.

Typical fields:

- `provider`, for example `auth0`
- `subject`, the stable IdP subject claim
- `user_id`
- `idp_tenant`

`(provider, subject)` is globally unique. `/api/v1/users/whoami` uses this link to resolve an Auth0 identity into a CP user.

If a first login arrives with an unknown Auth0 subject but a matching invited email, CP can create the AuthAccount link, activate the pending invitation, activate the workspace membership, and bump `rbac_revision`.

### WorkspaceMembership

A membership links a user to a workspace and role.

Typical fields:

- `workspace_id`
- `user_id`
- `role_id`
- `status`: active, invited, or suspended

This is the main answer to: "Can this person access this workspace?"

### Groups

Groups organize users inside a workspace.

Groups answer: "Which team or audience is this user part of?"

Roles answer: "What can this user do?"

DP may use groups for audience targeting or runtime behavior, but permissions come from roles and permission codes.

### Roles And Permissions

Roles are permission bundles. Permissions are concrete allowed actions.

Examples:

- `users.view`
- `workspace_settings.manage`
- `sso.manage`
- `data_sources.production_postgres.query`

Roles can be system templates or custom roles. Permission changes bump the workspace RBAC revision so DP can invalidate cached authorization data.

### RBAC Revision

Each workspace has an integer `rbac_revision`.

It increments when permission-affecting state changes, such as:

- Role permission updates.
- Membership role assignment changes.
- Membership create/delete/status changes.
- Invitation first-login activation.
- Data-source permission creation/deletion.

DP should cache `whoami` data by user/workspace but refresh when `rbac_revision` changes.

Endpoint:

```text
GET /api/v1/workspaces/{workspace_id}/rbac-revision
Authorization: Bearer <CP_API_TOKEN>
```

Response:

```json
{
  "revision": 7,
  "updated_at": "2026-05-08T12:34:56Z"
}
```

## 4. Data Sources

CP Data Sources are currently a registry and permission surface.

CP stores:

- Source slug.
- Source type.
- Display name and icon.
- Status mirror.
- Last sync status/time reported by DP.
- Source-specific permission code.

Example:

```text
Display name: Production Postgres
Slug: production_postgres
Permission: data_sources.production_postgres.query
```

CP does not store customer database credentials, run OAuth connector flows by itself, execute SQL, or sync customer data. DP owns those runtime behaviors.

### CP Data-Source Endpoints

Admin registry:

```text
GET  /api/v1/workspaces/{workspace_id}/data-sources
POST /api/v1/workspaces/{workspace_id}/data-sources
GET  /api/v1/workspaces/{workspace_id}/data-sources/overview
GET  /api/v1/data-sources/{data_source_id}
PATCH /api/v1/data-sources/{data_source_id}
DELETE /api/v1/data-sources/{data_source_id}
GET  /api/v1/data-sources/{data_source_id}/sync-log
```

Public display surface for DP/UI consumers:

```text
GET /api/v1/workspaces/{workspace_id}/data-sources/public
```

DP heartbeat/status mirror:

```text
POST /internal/v1/data-sources/{data_source_id}/status
Authorization: Bearer <CP_API_TOKEN>
```

### How DP Should Enforce Data-Source Access

When a user asks DP to query `production_postgres`, DP should check:

1. License is valid for the deployment.
2. User is active in the target workspace.
3. `whoami.permissions` includes `data_sources.production_postgres.query`.
4. DP connector registry has a configured connector for this workspace and slug.
5. Query execution is scoped to the licensed organization/workspace.

## 5. Billing, Deployments, And License JWTs

### Plan

A plan is a feature tier.

It contains default features and limits, for example:

- Seat caps.
- Feature flags.
- Workspace limits.
- Export or alert enablement.

### Subscription

A subscription attaches an organization to a plan.

It can include:

- Status.
- Seat cap override.
- Feature overrides.
- Start/end/trial dates.
- External billing ID.

### Deployment

A deployment is a DP runtime instance.

Examples:

- Customer production cloud DP.
- Customer on-prem DP.
- Demo DP.

The deployment ID appears in the license JWT so DP knows which deployment it is.

### License

A license is the signed entitlement for a deployment.

The JWT payload uses `odcp_contracts.licensing.DeploymentLicenseClaims` and contains:

- Standard JWT claims: `iss`, `aud`, `sub`, `jti`, `iat`, `nbf`, `exp`.
- License body with organization, deployment, subscription, plan, features, and seat cap.
- Branding snapshots.
- Workspace settings snapshot.
- `cp_base_url`.
- `rbac_endpoint`.

DP verifies the license at startup and before serving customer traffic.

JWKS endpoint:

```text
GET /api/v1/.well-known/jwks.json
```

Signing diagnostics:

```text
GET /api/v1/signing/info
Authorization: Bearer <CP_API_TOKEN>
```

## 6. Whoami Flow

Endpoint:

```text
GET /api/v1/users/whoami?provider=auth0&subject=<auth0_sub>&email=<email>
Authorization: Bearer <CP_API_TOKEN>
```

CP returns the v2 `WhoAmIResponse` contract:

```jsonc
{
  "user": {
    "id": "...",
    "email": "anna@example.com",
    "display_name": "Anna",
    "is_active": true,
    "is_superadmin": false
  },
  "superadmin": false,
  "workspaces": [
    {
      "workspace_id": "...",
      "workspace_slug": "acme_prod",
      "workspace_name": "Acme Production",
      "organization_id": "...",
      "organization_slug": "acme",
      "status": "active",
      "role": { "code": "ANALYST", "name": "Analyst" },
      "permissions": ["data_sources.production_postgres.query"],
      "groups": [{ "id": "...", "name": "Finance Analysts" }],
      "rbac_revision": 7
    }
  ],
  "rbac_revision": {
    "<workspace_id>": 7
  }
}
```

DP should use this response as its runtime authorization input. CP decides policy; DP enforces it.

## 7. Security And Admin Auth

CP supports both human admin calls and machine calls.

Human admin calls:

- Use Auth0 bearer tokens.
- Resolve to a local CP user.
- Enforce superadmin, workspace membership, and permission checks.
- Participate in session/IP allowlist/security middleware where applicable.

Machine calls:

- Use `Authorization: Bearer <CP_API_TOKEN>`.
- Are treated as trusted service/superadmin calls where accepted.
- Are used by DP and internal automation.

Public calls:

- JWKS.
- Public branding snapshot.
- Public data-source display surface.
- Invitation accept links.
- SSO test callback with signed state.
- Notification-channel OAuth callback with signed state.

For detailed endpoint security behavior, see `docs/howtos/CP_API_SECURITY.md`.

## 8. Security Settings

CP owns organization/workspace security configuration:

- SSO providers.
- SSO test and enforcement flow.
- Password policy.
- Session settings.
- IP allowlist.
- Workspace retention settings.

SSO admin routes accept unified admin identity. A machine caller can operate them for setup/smoke testing, while normal Auth0 users need `sso.manage` or superadmin status.

The public SSO test callback is not bearer-authenticated; the signed state token is the credential and the callback is rate-limited.

## 9. Branding

Branding is workspace/customer presentation metadata.

CP stores branding snapshots and exposes them through:

- Authenticated admin endpoints for editing.
- Public ETag-cached endpoints for consumers.
- License claims as fallback/snapshot data.

DP may use CP branding directly or consume the branding snapshot in the license, depending on runtime needs and cache behavior.

## 10. Notification Centre And Channels

Notification Centre owns message intent and history:

- Broadcasts.
- Recurring notifications.
- Transactional notification event definitions.
- Delivery history.
- User notification preferences.

Notification Channels own delivery configuration:

- Email/SMTP.
- Slack.
- Microsoft Teams.
- OAuth callback state and provider connection status.

DP can emit transactional events to CP through a bearer-token-protected endpoint. CP records delivery attempts and exposes history for operators.

## 11. Audit Log

CP audit logs capture administrative control-plane actions.

Use audit logs for:

- Who changed user access.
- Who edited roles/permissions.
- Who changed workspace/security/branding/data-source settings.
- Evidence during customer or internal reviews.

Do not treat CP audit logs as the full DP runtime event log. DP should keep its own runtime query/chat/dashboard audit where needed.

## 12. Deployment Topology

Production target is a single GCE VM running Docker Compose. Kubernetes is not part of the current deployment target.

Services:

| Service | Purpose |
|---------|---------|
| `cp_db` | Postgres 16 database |
| `cp_api` | FastAPI backend |
| `cp_frontend` | React/Vite admin UI served by nginx |
| `caddy` | TLS termination and reverse proxy |

Caddy routes:

- `/api/*` to `cp_api:8801`.
- `/*` to `cp_frontend:80`.

Deployment files:

```text
deploy/docker-compose.prod.yml
deploy/Caddyfile
deploy/bootstrap.sh
```

The signing keypair must be provisioned into the expected secret volume/path before CP starts in `RSA_PEM` mode.

## 13. Local Development

Full stack:

```bash
cp .env.example .env
docker compose up --build
```

Backend only:

```bash
pip install -r requirements.txt -r requirements-dev.txt -r requirements-test.txt
alembic upgrade head
uvicorn app.main:app --reload --port 8801
```

Frontend only:

```bash
cd frontend
npm install
npm run dev
```

Common local ports:

- API: `8801`
- Postgres: `5435`
- Frontend nginx: `3001`
- Caddy dev entry point: `8080`

## 14. Contract Package

`odcp_contracts` contains the public CP-DP schemas, especially:

- `DeploymentLicenseClaims`
- `LicenseBodyClaims`
- `WhoAmIResponse`

Treat this package as a versioned contract. Backward-incompatible changes require a major version bump. Backward-compatible additions require at least a minor bump.

DP should depend on the published contract package or the future `odcp_client` SDK rather than duplicating claim and `whoami` parsing by hand.

## 15. Current Mental Model

CP decides and records policy.

DP enforces policy at runtime.

Auth0 proves user identity.

For any access decision, ask:

1. Does the license allow this DP deployment to run?
2. Does Auth0 prove the user identity?
3. Does CP say the user is active in this workspace?
4. Does CP say the user has the required permission?
5. Does DP have the runtime connector/data capability to execute the request?

Only when all relevant answers are yes should DP allow the action.
