feat: add database-driven feature flag library (OHE-3101) - #217
Conversation
OHE-3101 [Hardening Week] Add a first-class feature flag library for safer releases
Proposed solutions: adopt before buildingPrefer an established open-source library/control plane rather than implementing feature flags from scratch.
Recommended starting point: use OpenFeature as the typed application API, then evaluate PostHog, Unleash, GrowthBook, or Flagsmith as the runtime provider/control plane. The Hardening Week deliverable should include a short build-vs-adopt decision and a small proof of concept with the leading option. SummaryOpenHands does not have a shared release-safety feature flag contract across Agent Canvas, backend services, SaaS, and self-hosted deployments. Flags currently appear as hardcoded web-client booleans, This is a good Hardening Week candidate because an initial library integration, safe provider contract, tests, release runbook, and two representative migrations are bounded work that improves every later release. ContextAudit performed against
Related but not duplicate:
Required propertiesWhichever library/provider is selected must support:
A client-visible flag must never be treated as authorization. Server endpoints must continue enforcing authentication, permission, entitlement, and data access independently. Potential implementation from scratchThe following is context and a fallback design if no existing library satisfies the requirements. It is not the preferred first choice. Typed registryconst flags = defineFeatureFlags({
hostedVscode: {
type: "boolean",
default: false,
owner: "agent-canvas",
failurePolicy: "use-default",
expiresAt: "2026-10-01",
},
newConversationFlow: {
type: "variant",
variants: ["control", "candidate"],
default: "control",
owner: "conversation-platform",
failurePolicy: "use-default",
expiresAt: "2026-10-01",
},
});Product code would consume a provider-neutral API: Provider adapters could include static defaults, test overrides, deployment configuration, backend-evaluated snapshots, and PostHog or another remote provider. Evaluation rules
Feature availability should compose separate concerns: Lifecycle
Bounded Hardening Week scope
Acceptance criteria
This issue was updated by an AI agent (OpenHands) on behalf of the user. |
Add a first-class feature flag system modeled on the existing user_authorizations whitelist/blacklist pattern: - FeatureFlag + FeatureFlagRule storage models with targeting by user_id, org_id, email_pattern (SQL LIKE), and percentage rollout - FeatureFlagStore async store mirroring user_authorization_store's _internal(session)/public(session=None) overload pattern - FeatureFlagService evaluator with exclude-before-include precedence (generalizes whitelist-beats-blacklist), deterministic percentage bucketing, and a short-TTL in-memory cache - Admin REST API at /api/admin/feature-flags gated by a new MANAGE_FEATURE_FLAGS permission granted only to the superadmin role - Alembic migration 150 (chained off 149, the current origin head) - 46 unit tests (store, service, routes) Co-authored-by: openhands <openhands@all-hands.dev>
… conv limit) origin/main landed migration 150_add_daily_conversation_limit after this branch was cut, so the feature-flags migration collided on revision 150. Renumber to 151 and chain off the new 150 head. Co-authored-by: openhands <openhands@all-hands.dev>
b74084e to
4a335f8
Compare
|
|
Resolve merge conflict in enterprise/server/auth/authorization.py by keeping both the new MANAGE_ORG_QUOTA permission (from main) and the MANAGE_FEATURE_FLAGS permission (from this branch), and adding both to the super-role permissions set. Renumber the feature-flags migration 151 -> 154 to chain after the new main migrations (151 org daily conv limit, 152 quota increase request, 153 kimi->deepseek settings migration). Single alembic head is now 154. Co-authored-by: openhands <openhands@all-hands.dev>
d6eae8b to
2334d35
Compare
HUMAN:
AGENT:
Why
There is no first-class feature flag mechanism in the application today; gating is done with env-var toggles and ad-hoc config. OHE-3101 asks for a database-driven, no-external-service flag library with user/org/email targeting, rule-based includes/excludes, and REST administration.
Surveying the Python ecosystem, the mature flag libraries (Unleash, Flagsmith, GrowthBook) all assume a separate flag-delivery service, which conflicts with the "no external service" requirement. This PR implements a bespoke library instead, modeled directly on the existing
user_authorizationswhitelist/blacklist pattern — so it's a natural generalization of a pattern the team already maintains.Summary
FeatureFlag+FeatureFlagRulestorage models with targeting byuser_id,org_id,email_pattern(SQL LIKE), and percentage rollout, plus an async store mirroringuser_authorization_store's_internal(session)/public(session=None)overload pattern.FeatureFlagServiceevaluator with exclude-before-include precedence (generalizes whitelist-beats-blacklist), deterministic percentage bucketing (sha256(flag+user)), and a short-TTL in-memory cache./api/admin/feature-flags(CRUD for flags + rules, plus an evaluate endpoint) gated by a newMANAGE_FEATURE_FLAGSpermission granted only to thesuperadminsuper role.150(chained off149, the current origin head) and 46 unit tests across store/service/routes.Issue Number
OHE-3101
How to Test
cd enterprise && poetry install --with dev,testPYTHONPATH=".:$PYTHONPATH" poetry run pytest enterprise/tests/unit/storage/test_feature_flag_store.py enterprise/tests/unit/server/services/test_feature_flag_service.py enterprise/tests/unit/server/routes/test_feature_flags.py150against a dev DB (alembic upgrade head), then exercise the REST endpoints (requires a superadmin caller):POST /api/admin/feature-flags→ create a flagPOST /api/admin/feature-flags/{key}/rules→ add an include/exclude rulePOST /api/admin/feature-flags/{key}/evaluate→ preview a contextVideo/Screenshots
N/A — backend-only change; behavior verified via the unit test suite (46 passing).
Type
Notes
150(not148) because148and149already landed onorigin/mainwhile this was in progress.MANAGE_FEATURE_FLAGSpermission is granted only to thesuperadminsuper role (parallel toMANAGE_SUPER_ADMINS); no org-scoped role can reach these routes.ondelete=CASCADEby default.Enterprise server image for this PR: