Skip to content

feat(licensing): send Customer Center a usage report every 30 minutes - #1114

Draft
jlav wants to merge 5 commits into
jl/license-enforcementfrom
jl/pltf-3967-usage-report
Draft

jlav wants to merge 5 commits into
jl/license-enforcementfrom
jl/pltf-3967-usage-report

Conversation

@jlav

@jlav jlav commented Nov 1, 2026 •

Copy link
Copy Markdown
Member

HUMAN:

  • A human has tested these changes.

AGENT:


Why

Staff need to see how each customer actually uses OHE: how many conversations, users and orgs, whether SSO and personal workspaces are on, and which sandbox it runs on. Customer Center now stores usage reports and shows them on the admin customer page.

It also needs to count installs. Several people at one company may each run their own install, so each report carries a stable instance ID.

This is the OHE side that sends those reports.

Summary

  • The worker runs a new usage_report job every 30 minutes. It's on whenever OPENHANDS_LICENSE_KEY is set, whether or not license enforcement is on, and has no OH_WORKER_SCHEDULED_JOBS_* switch.
  • Each report holds lifetime totals, so a missed or duplicate report loses nothing. A failed report is logged, and the next run is the retry.
  • The instance ID is a UUID in a new instance_settings.instance_id column. The first report creates it, and it never changes. Replicas that race to create it all get the first one.

Issue Number

PLTF-3967

How to Test

  1. Run Customer Center locally and get a license key from its dashboard.
  2. Migrate a local database (make local-db) and start the worker with OPENHANDS_LICENSE_KEY=<key> and LICENSE_SERVER_URL=<customer center>: make start-worker.
  3. The worker.started log line lists usage_report in scheduled_jobs. At the next :00 or :30 the log shows usage_report_sent.
  4. Open the customer's page under /admin in Customer Center. The install shows under its instance ID with the reported numbers.

Unit tests: uv run pytest tests/unit/server/licensing/test_usage_report.py tests/unit/storage/test_instance_settings_store.py tests/unit/app_server/test_worker_scheduled_jobs.py tests/unit/test_user_store.py.

Video/Screenshots

See Demo below.

Type

  • Bug fix
  • Feature
  • Refactor
  • Breaking change
  • Docs / chore

Notes

Where each field comes from:

  • conversations, users: row counts of conversation_metadata and user.
  • orgs: team orgs only. A personal workspace shares its id with its user, so it's already counted in users.
  • automations: sent as null (not reported). The automation service has no global count yet.
  • sso_enabled: ENABLE_ENTERPRISE_SSO, or with OAuth v2 login an enabled SAML or OIDC login provider. Keycloak's own rows and git providers don't count.
  • personal_workspaces_enabled: the web client's hide_personal_workspaces flag.
  • sandbox_strategy: the sandbox service in use (OH_SANDBOX_KIND, else RUNTIME).

The first user's insert into instance_settings now fills setup_user_id on an existing row. A report can create the row before anyone signs in, and the first-install wizard still needs that column.

Migration 207 adds the column. Docker Compose now passes OPENHANDS_LICENSE_KEY and LICENSE_SERVER_URL to the app and worker.

Helm and Replicated installs report only once the OpenHands-Cloud chart runs the worker; Docker Compose already does.

Stacked on #1112.

Demo

The worker runs the usage_report job against a local OHE database, and the report shows up on the customer's admin page in a local Customer Center, under the install's instance ID.

PLTF-3967-ohe-usage-report.webm

Enterprise server image for this PR:

ghcr.io/openhands/enterprise-server:sha-ba34dc5

@github-actions github-actions Bot added the type: feat A new feature label Nov 1, 2026
@github-actions

github-actions Bot commented Nov 1, 2026

Copy link
Copy Markdown

⚠️ This PR contains migrations. Please synchronize before merging to prevent conflicts.

@jlav
jlav added this pull request to stack #1115 November 1, 2026 16:20
@github-actions

github-actions Bot commented Nov 1, 2026

Copy link
Copy Markdown

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  migrations/versions
  207_add_instance_id_to_instance_settings.py 20, 27
  openhands/app_server/worker
  scheduled_jobs.py
  server/licensing
  usage_report.py 95
  storage
  instance_settings.py
  instance_settings_store.py 41, 46
  user_store.py
Project Total  

This report was generated by python-coverage-comment-action

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: feat A new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant