{"id":49484758,"url":"https://github.com/rendis/feature-evaluator","last_synced_at":"2026-05-01T00:30:35.391Z","repository":{"id":343511471,"uuid":"1177342029","full_name":"rendis/feature-evaluator","owner":"rendis","description":"Feature flag system with rule-based evaluation, segment targeting, and pack-based feature bundling","archived":false,"fork":false,"pushed_at":"2026-04-07T20:24:06.000Z","size":7670,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-07T22:26:25.573Z","etag":null,"topics":["feature-flags","feature-management","feature-toggles","go","postgresql","react","redis","rule-engine","segment-targeting","typescript"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rendis.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-09T23:56:38.000Z","updated_at":"2026-04-07T20:24:14.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/rendis/feature-evaluator","commit_stats":null,"previous_names":["rendis/feature-evaluator"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rendis/feature-evaluator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rendis%2Ffeature-evaluator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rendis%2Ffeature-evaluator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rendis%2Ffeature-evaluator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rendis%2Ffeature-evaluator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rendis","download_url":"https://codeload.github.com/rendis/feature-evaluator/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rendis%2Ffeature-evaluator/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32481553,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"ssl_error","status_checked_at":"2026-04-30T13:12:06.837Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["feature-flags","feature-management","feature-toggles","go","postgresql","react","redis","rule-engine","segment-targeting","typescript"],"created_at":"2026-05-01T00:30:34.499Z","updated_at":"2026-05-01T00:30:35.370Z","avatar_url":"https://github.com/rendis.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"docs/assets/branding/hero-banner.svg\"\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"docs/assets/branding/hero-banner.svg\"\u003e\n    \u003cimg alt=\"Feature Evaluator\" src=\"docs/assets/branding/hero-banner.svg\" width=\"100%\"\u003e\n  \u003c/picture\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://go.dev/\"\u003e\u003cimg src=\"https://img.shields.io/badge/Go-1.25-00ADD8?logo=go\u0026logoColor=white\" alt=\"Go 1.25\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://react.dev/\"\u003e\u003cimg src=\"https://img.shields.io/badge/React-19-61DAFB?logo=react\u0026logoColor=white\" alt=\"React 19\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://openfeature.dev/specification/appendix-c-ofrep/\"\u003e\u003cimg src=\"https://img.shields.io/badge/OFREP-compatible-6544e4\" alt=\"OFREP Compatible\"\u003e\u003c/a\u003e\n  \u003ca href=\"LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/License-MIT-green\" alt=\"MIT License\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  Self-hosted feature flag platform with expression-based rules, user segments, feature packs,\u003cbr\u003e\n  A/B experiments, and a full admin console. Built for teams that need fine-grained control\u003cbr\u003e\n  over progressive delivery without vendor lock-in.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"#quick-start\"\u003eQuick Start\u003c/a\u003e ·\n  \u003ca href=\"#key-features\"\u003eFeatures\u003c/a\u003e ·\n  \u003ca href=\"#architecture\"\u003eArchitecture\u003c/a\u003e ·\n  \u003ca href=\"#api-usage\"\u003eAPI Usage\u003c/a\u003e ·\n  \u003ca href=\"docs/api-reference.md\"\u003eAPI Reference\u003c/a\u003e ·\n  \u003ca href=\"docs/deployment.md\"\u003eDeployment\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n## Admin Console\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/assets/screenshots/dashboard.png\" alt=\"Dashboard — system health, stats, metrics, and recent activity\" width=\"100%\"\u003e\n\u003c/p\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eMore screenshots\u003c/strong\u003e\u003c/summary\u003e\n\u003cbr\u003e\n\n**Feature list** — search, filter by status/type/tag/environment, toggle features inline\n\n\u003cimg src=\"docs/assets/screenshots/feature-list.png\" alt=\"Feature list with filters, tags, and inline toggles\" width=\"100%\"\u003e\n\n**Segments** — user segment management with CSV import and tenant-scoped membership\n\n\u003cimg src=\"docs/assets/screenshots/segments.png\" alt=\"Segment list showing Beta Users, Enterprise Tenants, Early Adopters\" width=\"100%\"\u003e\n\n\u003c/details\u003e\n\n## Key Features\n\n**Evaluation Engine**\n\n- Expression-based rules powered by [expr-lang](https://github.com/expr-lang/expr) with compiled bytecode and LRU cache\n- Percentage rollouts with deterministic, monotonic user bucketing (FNV-1a)\n- Multi-environment targeting (dev, staging, production)\n- [OFREP](https://openfeature.dev/specification/appendix-c-ofrep/) compatible for vendor-agnostic SDK integration\n\n**Segments \u0026 Targeting**\n\n- Import thousands of users via CSV with tenant-scoped membership\n- Use `inSegment(\"beta-users\")` directly in rule expressions\n- Batch segment resolution — pre-scans all rules, resolves memberships in one pass\n\n**Feature Packs**\n\n- Bundle features into packs and activate per tenant, campus, or program\n- Pack inheritance chains and tier-based feature gating\n- Trial periods with automatic expiration\n\n**Experimentation**\n\n- Full A/B test lifecycle: draft → running → paused → completed\n- Deterministic variant assignment, exposure tracking, conversion reporting\n- Wilson score confidence intervals for statistical results\n\n**Admin Console**\n\n- React 19 SPA with dark/light theme and i18n (Spanish default, English)\n- RBAC with 4 roles: Owner, Admin, Editor, Viewer\n- Multi-workspace isolation — all entities scoped per workspace\n\n**Operations**\n\n- Scheduled rollouts — program future changes with background worker execution\n- Immutable change history with field-level diffs and actor tracking\n- External HTTP validation per rule with circuit breakers (fail-open/closed)\n- Rate limiting and Redis caching (fail-open design)\n\n## Architecture\n\n```mermaid\ngraph TD\n    SDK[\"Client / SDK\"] --\u003e|POST /features/eval| API\n    OFSDK[\"OpenFeature SDK\"] --\u003e|POST /ofrep/v1/evaluate/flags| API\n    Console[\"Admin Console\u003cbr/\u003eReact 19 + TanStack\"] --\u003e|/features/admin/*| API\n\n    API[\"Feature Evaluator\u003cbr/\u003eGo 1.25 · Gin\"]\n    API --\u003e Engine[\"Rule Engine\u003cbr/\u003eexpr-lang · LRU cache\"]\n    API --\u003e PG[\"PostgreSQL\u003cbr/\u003eFeatures · Rules · Segments\"]\n    API --\u003e Redis[\"Redis\u003cbr/\u003eCache · Rate limit\"]\n    Engine -.-\u003e|Optional| Ext[\"External APIs\u003cbr/\u003eCircuit breaker protected\"]\n    API -.- Worker[\"Schedule Worker\u003cbr/\u003e30s polling · skip-locked\"]\n    Worker --\u003e PG\n```\n\n### Evaluation Pipeline\n\n```mermaid\nflowchart LR\n    A[\"Lookup\\nfeature\"] --\u003e B[\"Auth\\nprofile\"]\n    B --\u003e C[\"Enabled?\"]\n    C --\u003e D[\"Schedule\\ngates\"]\n    D --\u003e E[\"Environment\\nfilter\"]\n    E --\u003e F[\"Trial\\ncheck\"]\n    F --\u003e G[\"Experiment\\noverride\"]\n    G --\u003e H[\"Resolve\\nsegments\"]\n    H --\u003e I[\"Pack\\nactivation\"]\n    I --\u003e J[\"Evaluate\\nrules\"]\n    J --\u003e K[\"Default\\nvalue\"]\n```\n\n\u003e Rules are evaluated by priority (ascending). First match wins. Each rule can have rollout percentage, external API bindings, and source bindings. If no rule matches, the feature's `defaultValue` is returned.\n\n## Tech Stack\n\n| Layer       | Technology                                                                                   |\n| ----------- | -------------------------------------------------------------------------------------------- |\n| Backend     | Go 1.25, Gin, PostgreSQL (pgx/v5), Redis                                                     |\n| Rule Engine | [expr-lang/expr](https://github.com/expr-lang/expr) — compiled bytecode, 10K-entry LRU cache |\n| Frontend    | React 19, Vite 7, TypeScript 5.9, TanStack Router + Query                                    |\n| UI          | Tailwind CSS v4, shadcn/ui, Radix UI, OKLCH theming                                          |\n| Auth        | OIDC (any provider), PKCE S256, API keys                                                     |\n| State       | Zustand (client), TanStack Query (server)                                                    |\n| i18n        | react-i18next — Spanish (default), English                                                   |\n| Protocol    | OFREP (OpenFeature Remote Evaluation Protocol)                                               |\n\n## Quick Start\n\n**Prerequisites:** Go 1.25+, Node 22+, pnpm, PostgreSQL, Docker\n\n```bash\n# 1. Start Redis\nmake redis\n\n# 2. Configure backend (copy and edit server/.env)\ncp server/.env.example server/.env\n# Set DATABASE_URL, REDIS_URI, and generate AUTH_SECRETS_MASTER_KEY:\n#   openssl rand -base64 32\n\n# 3. Start backend (port 8080) + frontend (port 5173)\nmake dev\n```\n\n\u003e **Dev mode:** Auth is disabled by default (`AUTH_DISABLED=true`). A mock user with `owner` role is injected automatically. The backend runs a preflight check and fails fast if PostgreSQL or Redis are unreachable.\n\n## API Usage\n\n### Single Feature Evaluation\n\n```bash\ncurl -X POST http://localhost:8080/features/eval \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"featureKey\": \"checkout-v2\",\n    \"context\": {\n      \"user\": { \"id\": \"u-123\", \"plan\": \"enterprise\" },\n      \"tenant\": { \"id\": \"acme-corp\" }\n    },\n    \"environment\": \"production\"\n  }'\n```\n\n```jsonc\n// Response\n{\n  \"featureKey\": \"checkout-v2\",\n  \"value\": true,\n  \"valueType\": \"boolean\",\n  \"reason\": \"matched_rule\",\n  \"matchedRule\": { \"name\": \"Enterprise users\", \"priority\": 0 },\n  \"segments\": [{ \"key\": \"beta-users\", \"isMember\": true }],\n  \"evaluatedAt\": \"2026-03-10T12:00:00Z\",\n}\n```\n\n### Bulk Evaluation\n\n```bash\ncurl -X POST http://localhost:8080/features/eval/bulk \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"features\": [\n      { \"featureKey\": \"checkout-v2\", \"context\": { \"user\": { \"id\": \"u-123\" } } },\n      { \"featureKey\": \"dark-mode\", \"context\": { \"user\": { \"id\": \"u-123\" } } }\n    ]\n  }'\n```\n\n### OFREP (OpenFeature)\n\n```bash\ncurl -X POST http://localhost:8080/ofrep/v1/evaluate/flags/checkout-v2 \\\n  -H \"Content-Type: application/json\" \\\n  -d '{ \"context\": { \"targetingKey\": \"u-123\", \"tenantId\": \"acme-corp\" } }'\n```\n\n\u003e OFREP flat context is auto-mapped: `targetingKey` → `user.id`, `tenantId` → `tenant.id`. See [API Reference](docs/api-reference.md) for all endpoints.\n\n## Expression Engine\n\nRules use [expr-lang](https://github.com/expr-lang/expr) expressions with these built-in variables and functions:\n\n| Context             | Available Fields                                              |\n| ------------------- | ------------------------------------------------------------- |\n| `user`              | `user.id`, `user.plan`, `user.email`, ... (from eval context) |\n| `tenant`            | `tenant.id`, `tenant.name`, ...                               |\n| `campus`, `program` | Same pattern as tenant                                        |\n| `authenticated`     | `true` if request passed auth profile validation              |\n\n| Function                          | Description                        |\n| --------------------------------- | ---------------------------------- |\n| `inSegment(\"key\")`                | Check if user belongs to a segment |\n| `now()`                           | Current time                       |\n| `dateBefore(field, \"2026-12-31\")` | Date comparison                    |\n| `dateAfter(field, \"2026-01-01\")`  | Date comparison                    |\n\n**Example expressions:**\n\n```\nuser.plan == \"enterprise\" \u0026\u0026 inSegment(\"beta-users\")\n```\n\n```\nauthenticated \u0026\u0026 tenant.id in [\"acme\", \"globex\"] \u0026\u0026 dateAfter(now(), \"2026-06-01\")\n```\n\n\u003e Full guide: [Rule Engine Documentation](docs/rule-engine.md)\n\n## Project Structure\n\n```\nfeature-evaluator/\n├── server/                        # Go backend\n│   ├── cmd/server/                # Entrypoint + preflight checks\n│   ├── internal/\n│   │   ├── domain/                # Business logic (zero external deps)\n│   │   │   ├── evaluation/        # Eval pipeline + rollout bucketing\n│   │   │   ├── feature/           # Feature + rule CRUD\n│   │   │   ├── pack/              # Feature packs + activations\n│   │   │   ├── segment/           # Segments + membership\n│   │   │   ├── experiment/        # A/B testing lifecycle\n│   │   │   ├── schedule/          # Scheduled rollouts + worker\n│   │   │   └── ...                # member, apikey, tag, changelog, workspace, tier\n│   │   ├── engine/                # expr-lang compiler, LRU cache, security\n│   │   ├── external/              # HTTP validation + circuit breaker\n│   │   ├── handler/               # Gin HTTP handlers (DTO mapping only)\n│   │   ├── dto/                   # Request/response types + mappers\n│   │   ├── storage/postgres/      # Repository implementations\n│   │   └── server/                # Routes + middleware stack\n│   └── pkg/apierror/              # Structured API errors (code + i18n messageKey)\n├── console/                       # React frontend\n│   └── src/\n│       ├── routes/                # TanStack Router (file-based)\n│       ├── components/            # Features, rules, segments, packs, experiments, dashboard\n│       ├── api/                   # Typed API client\n│       ├── auth/                  # OIDC + RBAC guards\n│       ├── queries/ mutations/    # TanStack Query hooks\n│       └── stores/                # Zustand (workspace, sidebar)\n├── docs/                          # Architecture, API reference, MCP setup, deployment\n└── Makefile                       # All dev commands\n```\n\n## Commands\n\n| Command          | Description                                                     |\n| ---------------- | --------------------------------------------------------------- |\n| `make dev`       | Start backend + frontend (PostgreSQL and Redis must be running) |\n| `make server`    | Go backend on port 8080                                         |\n| `make console`   | React frontend on port 5173                                     |\n| `make redis`     | Start Redis via Docker Compose                                  |\n| `make quality`   | Full CI gate: lint + test + typecheck                           |\n| `make lint`      | Go (golangci-lint) + React (ESLint)                             |\n| `make test`      | Go (with `-race`) + React (Vitest)                              |\n| `make typecheck` | TypeScript type checking                                        |\n| `make fmt`       | gofmt + goimports + Prettier                                    |\n| `make swagger`   | Regenerate OpenAPI spec from handler annotations                |\n\n## API Routes\n\n| Group       | Auth                 | Key Routes                                                               |\n| ----------- | -------------------- | ------------------------------------------------------------------------ |\n| Health      | None                 | `GET /features/healthz`, `/features/readyz`                              |\n| Eval        | Bearer / API key     | `POST /features/eval`, `/eval/bulk`, `/eval/active`, `/eval/conversions` |\n| OFREP       | Feature auth profile | `POST /ofrep/v1/evaluate/flags/:key`, `/ofrep/v1/evaluate/flags`         |\n| Features    | JWT (admin)          | `GET/POST/PUT/DELETE /features/admin/features`, `PATCH .../toggle`       |\n| Rules       | JWT (admin)          | `CRUD /features/admin/features/:key/rules`, `PUT .../reorder`            |\n| Packs       | JWT (admin)          | `CRUD /features/admin/packs`, `POST/DELETE .../activate`                 |\n| Segments    | JWT (admin)          | `CRUD /features/admin/segments`, `POST .../members/import`               |\n| Experiments | JWT (admin)          | `CRUD /features/admin/experiments`, lifecycle transitions                |\n| Schedules   | JWT (admin)          | `POST/GET/DELETE /features/admin/features/:key/schedules`                |\n| Changelog   | JWT (admin)          | `GET /features/admin/changelog`                                          |\n| Workspaces  | JWT (owner)          | `CRUD /features/admin/workspaces`, archive/restore                       |\n| Members     | JWT (admin)          | `CRUD /features/admin/members`, ownership transfer                       |\n| Dashboard   | JWT (admin)          | `GET /features/admin/dashboard/{stats,activity,metrics,...}`             |\n\n## Roles \u0026 Permissions\n\n| Permission               | Owner | Admin | Editor | Viewer |\n| ------------------------ | :---: | :---: | :----: | :----: |\n| features.read            |   Y   |   Y   |   Y    |   Y    |\n| features.write           |   Y   |   Y   |   Y    |   —    |\n| segments.read / write    |   Y   |   Y   |   Y    | Y / —  |\n| packs.read / write       |   Y   |   Y   |   Y    | Y / —  |\n| experiments.read / write |   Y   |   Y   |   Y    | Y / —  |\n| members.manage           |   Y   |   Y   |   —    |   —    |\n| settings.manage          |   Y   |   Y   |   —    |   —    |\n| workspace.delete         |   Y   |   —   |   —    |   —    |\n| ownership.transfer       |   Y   |   —   |   —    |   —    |\n\n## Documentation\n\n| Document                               | Description                                 |\n| -------------------------------------- | ------------------------------------------- |\n| [Architecture](docs/architecture.md)   | System design, data flow, caching strategy  |\n| [API Reference](docs/api-reference.md) | Full endpoint reference with examples       |\n| [Configuration](docs/configuration.md) | All environment variables and setup         |\n| [Rule Engine](docs/rule-engine.md)     | Expression language, functions, security    |\n| [Deployment](docs/deployment.md)       | Docker, production checklist, health checks |\n| [UI Flows](docs/UI-FLOW.md)            | Admin console screen-by-screen walkthrough  |\n\n## MCP Integration\n\nUses `mcp-openapi-proxy` — auto-generates MCP tools from the Swagger spec. Each API endpoint becomes a callable MCP tool.\n\nInstall: `go install github.com/rendis/mcp-openapi-proxy/cmd/mcp-openapi-proxy@latest`\n\nSee [`docs/mcp_setup.md`](docs/mcp_setup.md) for full setup (Claude Code, Codex, Gemini CLI) and [`skills/feature-evaluator/SKILL.md`](skills/feature-evaluator/SKILL.md) for tool reference.\n\n## Deployment\n\nBoth backend and frontend include production-ready multi-stage Dockerfiles:\n\n- **Backend:** Go binary on Alpine 3.21, non-root user, port 8080\n- **Frontend:** Vite build → Nginx 1.27 Alpine, SPA routing, API proxy, port 80\n\n```bash\ndocker build -t feature-evaluator-server -f server/Dockerfile server/\ndocker build -t feature-evaluator-console -f console/Dockerfile console/\n```\n\n\u003e Full guide: [Deployment Documentation](docs/deployment.md)\n\n## Contributing\n\n1. Fork the repository\n2. Create a feature branch\n3. Run `make quality` before submitting (lint + test + typecheck)\n4. Open a pull request\n\n**Conventions:** Clean architecture (domain has zero external deps), handlers only map DTOs, errors use `pkg/apierror` with i18n messageKeys, Go errors wrapped with context (`fmt.Errorf(\"doing X: %w\", err)`).\n\n## License\n\nMIT — see [LICENSE](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frendis%2Ffeature-evaluator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frendis%2Ffeature-evaluator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frendis%2Ffeature-evaluator/lists"}