{"id":47895174,"url":"https://github.com/gyaan/fluxflow","last_synced_at":"2026-04-04T03:40:34.486Z","repository":{"id":339736731,"uuid":"1163173985","full_name":"gyaan/fluxflow","owner":"gyaan","description":"High-performance YAML-driven IFTTT rule engine for loyalty programmes. DAG evaluation, hot-reload, expression language, Prometheus metrics — built in Go.","archived":false,"fork":false,"pushed_at":"2026-02-21T12:19:34.000Z","size":86,"stargazers_count":0,"open_issues_count":4,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-02-21T15:26:37.577Z","etag":null,"topics":["dag","event-driven","go","golang","hot-reload","ifttt","loyalty-programme","points-engine","prometheus","rule-engine","worker-pool","yaml"],"latest_commit_sha":null,"homepage":"https://github.com/gyaan/fluxflow","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/gyaan.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-21T07:53:15.000Z","updated_at":"2026-02-21T12:19:37.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/gyaan/fluxflow","commit_stats":null,"previous_names":["gyaan/fluxflow"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/gyaan/fluxflow","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gyaan%2Ffluxflow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gyaan%2Ffluxflow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gyaan%2Ffluxflow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gyaan%2Ffluxflow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gyaan","download_url":"https://codeload.github.com/gyaan/fluxflow/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gyaan%2Ffluxflow/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31387023,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T01:22:39.193Z","status":"online","status_checked_at":"2026-04-04T02:00:07.569Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["dag","event-driven","go","golang","hot-reload","ifttt","loyalty-programme","points-engine","prometheus","rule-engine","worker-pool","yaml"],"created_at":"2026-04-04T03:40:33.752Z","updated_at":"2026-04-04T03:40:34.480Z","avatar_url":"https://github.com/gyaan.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# FluxFlow — IFTTT Rule Engine\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/gyaan/fluxflow/actions/workflows/ci.yml\"\u003e\n    \u003cimg src=\"https://github.com/gyaan/fluxflow/actions/workflows/ci.yml/badge.svg\" alt=\"CI\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/gyaan/fluxflow/releases/latest\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/v/release/gyaan/fluxflow\" alt=\"Latest release\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://pkg.go.dev/github.com/gyaneshwarpardhi/ifttt\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/go-1.23%2B-00ADD8?logo=go\" alt=\"Go version\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"LICENSE\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/license/gyaan/fluxflow\" alt=\"License: MIT\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/gyaan/fluxflow/releases\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/downloads/gyaan/fluxflow/total\" alt=\"Downloads\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  A high-performance, YAML-driven rule engine for loyalty programmes built in Go.\u003cbr\u003e\n  Events are evaluated against a DAG of configurable rules and trigger actions — with zero-downtime hot-reload.\n\u003c/p\u003e\n\n---\n\n## Features\n\n- **DAG evaluation** — depth-first traversal with early branch pruning; untouched subtrees cost zero CPU\n- **YAML rules** — no redeployment needed; change a file, rules update in under a second\n- **Atomic hot-reload** — `atomic.Pointer[dag.Graph]` swap; zero locks on the read path\n- **Expression language** — `payload.amount \u003e 1000 AND payload.category == \"food\"` compiled to AST once at startup\n- **Formula actions** — `points_formula: \"payload.amount * 0.05\"` evaluated at runtime against event payload\n- **Worker pool** — 32 fixed goroutines, 10k-slot bounded queue, HTTP 429 backpressure\n- **Prometheus metrics** — counters, histograms, queue utilisation gauge; scrape `/metrics`\n- **Pluggable actions** — one interface, one registration line; add webhooks, email, CRM with no engine changes\n\n---\n\n## How it works\n\n```\nHTTP POST /v1/events\n        │\n        ▼\n┌───────────────────┐\n│   API Handler     │  JSON decode · auto-fill ID · validate\n└────────┬──────────┘\n         │\n         ▼  non-blocking channel submit (429 if full)\n┌───────────────────────────────────────┐\n│   Event Queue  ░░░░░░░░  cap=10,000  │\n└────────┬──────────────────────────────┘\n         │\n    ┌────┴────┐  32 goroutines (pre-allocated, zero alloc per event)\n    ▼         ▼\n worker    worker  …\n    │\n    │  atomic.Pointer[Graph].Load()  — no lock\n    ▼\n┌──────────────────────────────────────────────┐\n│  DAG — Depth-First Evaluation                │\n│                                              │\n│  ScenarioNode (event.type + source filter)   │\n│  └── ConditionNode  (AST eval, short-circuit)│\n│       └── ConditionNode                      │\n│            └── ActionNode  ← leaf: execute   │\n└──────────────────────────────────────────────┘\n         │\n         ▼\n  ActionExecutor.Execute()\n  (reward_points, webhook, …)\n         │\n         ▼\n  EventResult → HTTP response\n```\n\n**Rules are a YAML tree → compiled to an immutable DAG at startup (or reload). No parsing at eval time.**\n\n---\n\n## Quick start\n\n```bash\ngit clone https://github.com/gyaan/fluxflow.git\ncd fluxflow\nCGO_ENABLED=0 go run cmd/server/main.go\n```\n\n```bash\n# High-value food transaction → 75 bonus points (1500 × 0.05)\ncurl -s -X POST http://localhost:8080/v1/events \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"type\":\"transaction\",\"source\":\"pos-system\",\"actor_id\":\"user_42\",\n       \"payload\":{\"amount\":1500,\"category\":\"food\"}}'\n```\n\n```json\n{\n  \"event_id\": \"...\",\n  \"duration_ms\": 0,\n  \"scenarios_matched\": [\"sc_high_value_food\"],\n  \"actions_executed\": [\n    {\n      \"action_id\": \"act_bonus_points\",\n      \"type\": \"reward_points\",\n      \"success\": true,\n      \"message\": \"Awarded 75 points to user_42 — High-value food purchase bonus\"\n    }\n  ]\n}\n```\n\nOr download a pre-built binary from the [Releases page](https://github.com/gyaan/fluxflow/releases/latest):\n\n```bash\n# Linux amd64\ncurl -Lo fluxflow https://github.com/gyaan/fluxflow/releases/latest/download/fluxflow-v0.1.0-linux-amd64\nchmod +x fluxflow \u0026\u0026 ./fluxflow\n```\n\n\u003e **macOS without Xcode tools:** prefix all `go` commands with `CGO_ENABLED=0`.\n\n---\n\n## Installation\n\n### From source\n\n```bash\nCGO_ENABLED=0 go install github.com/gyaneshwarpardhi/ifttt/cmd/server@latest\n```\n\n### Pre-built binaries\n\nAvailable for Linux (`amd64`, `arm64`), macOS (`amd64`, `arm64`), and Windows (`amd64`) on the [Releases page](https://github.com/gyaan/fluxflow/releases). SHA-256 checksums are included.\n\n---\n\n## Project layout\n\n```\nfluxflow/\n├── cmd/server/main.go                  # Entry point\n├── internal/\n│   ├── event/event.go                  # Canonical Event struct\n│   ├── config/                         # YAML schema · loader · validator\n│   ├── condition/                      # Tokenizer · AST parser · evaluator\n│   ├── dag/                            # Graph · builder · DFS evaluator\n│   ├── action/                         # Executor interface · registry · reward_points\n│   ├── engine/                         # Worker pool · atomic graph swap\n│   ├── api/                            # HTTP handlers · middleware\n│   └── metrics/                        # Prometheus instrumentation\n├── configs/rules.yaml                  # Example rules\n├── README.md · TEST.md · DEEPDIVE.md · CHANGELOG.md · CONTRIBUTING.md\n└── go.mod\n```\n\n---\n\n## Configuration\n\n### Server flags\n\n| Flag | Default | Description |\n|------|---------|-------------|\n| `-addr` | `:8080` | HTTP listen address |\n| `-config` | `configs/rules.yaml` | Path to YAML rules file |\n\n### Engine tuning\n\n```yaml\n# configs/rules.yaml\nengine:\n  event_workers: 32       # goroutines evaluating events\n  action_workers: 16      # goroutines running I/O-bound actions\n  queue_depth: 10000      # max events buffered (429 when full)\n  event_timeout_ms: 5000  # sync response timeout\n  fail_open: true         # on condition error, skip branch (don't fail event)\n```\n\n### Writing rules\n\n```yaml\nversion: v1\nscenarios:\n  - id: sc_vip_cashback\n    description: \"10% cashback for VIP members on electronics\"\n    enabled: true\n    event_types: [transaction]\n    sources: [mobile-app, web]        # omit to match all sources\n    children:\n      - condition:\n          id: cond_is_vip\n          expression: 'meta.tier == \"vip\"'\n          children:\n            - condition:\n                id: cond_is_electronics\n                expression: 'payload.category == \"electronics\"'\n                children:\n                  - action:\n                      id: act_vip_cashback\n                      type: reward_points\n                      params:\n                        operation: award\n                        points_formula: \"payload.amount * 0.10\"\n                        reason: \"VIP electronics cashback\"\n```\n\nNo restart required — save the file or call `POST /v1/rules/reload`.\n\n### Expression language\n\n| Operator | Types | Example |\n|----------|-------|---------|\n| `==` `!=` | any | `payload.category == \"food\"` |\n| `\u003e` `\u003e=` `\u003c` `\u003c=` | numeric | `payload.amount \u003e 1000` |\n| `contains` | string | `payload.tags contains \"vip\"` |\n| `matches` | string (regex) | `payload.email matches \".*@corp\\\\.com\"` |\n| `AND` `OR` `NOT` | boolean | `A AND (B OR NOT C)` |\n\nField namespaces: `payload.*` · `meta.*` · `event.type` · `event.source` · `event.actor_id`\n\nFormula arithmetic: `*` `/` `+` `-` (used in `points_formula` params)\n\n---\n\n## HTTP API\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `POST` | `/v1/events` | Ingest one event — synchronous, returns full result |\n| `POST` | `/v1/events/batch` | Ingest up to 100 events — async, returns job summary |\n| `GET` | `/v1/rules` | List loaded scenarios |\n| `POST` | `/v1/rules/reload` | Hot-reload rules from disk |\n| `GET` | `/healthz` | Liveness probe (always 200) |\n| `GET` | `/readyz` | Readiness probe (503 if queue \u003e80%) |\n| `GET` | `/metrics` | Prometheus metrics |\n\n\u003cdetails\u003e\n\u003csummary\u003eRequest / response examples\u003c/summary\u003e\n\n**POST /v1/events**\n\n```json\n// Request\n{\n  \"id\": \"evt_01\",\n  \"type\": \"transaction\",\n  \"source\": \"pos-system\",\n  \"actor_id\": \"user_42\",\n  \"payload\": { \"amount\": 1500, \"category\": \"food\" },\n  \"meta\": { \"tenant\": \"acme-retail\" }\n}\n\n// Response 200\n{\n  \"event_id\": \"evt_01\",\n  \"duration_ms\": 1,\n  \"scenarios_matched\": [\"sc_high_value_food\"],\n  \"actions_executed\": [\n    { \"action_id\": \"act_bonus_points\", \"type\": \"reward_points\",\n      \"success\": true, \"message\": \"Awarded 75 points to user_42 — High-value food purchase bonus\" }\n  ]\n}\n\n// Response 429 — queue full\n{ \"error\": \"event queue full (capacity 10000)\" }\n```\n\n**POST /v1/events/batch**\n\n```json\n// Request — array of up to 100 events\n[{ \"type\": \"login\", \"actor_id\": \"u1\", \"payload\": { \"is_first_login\": true } }]\n\n// Response 202\n{ \"job_id\": \"550e8400-...\", \"total\": 1, \"queued\": 1, \"rejected\": 0 }\n```\n\n\u003c/details\u003e\n\n---\n\n## Adding a new action type\n\nImplement one interface, register it — no other changes:\n\n```go\n// internal/action/webhook/notify.go\ntype NotifyAction struct{ client *http.Client }\n\nfunc (n *NotifyAction) Type() string { return \"webhook_notify\" }\n\nfunc (n *NotifyAction) Validate(params map[string]interface{}) error {\n    if _, ok := params[\"url\"].(string); !ok {\n        return fmt.Errorf(\"url is required\")\n    }\n    return nil\n}\n\nfunc (n *NotifyAction) Execute(ctx context.Context, id string,\n    params map[string]interface{}, evalCtx *dag.EvalContext) (*action.ActionResult, error) {\n    // POST to params[\"url\"]\n}\n```\n\n```go\n// cmd/server/main.go\nreg.Register(webhook.New())\n```\n\n```yaml\n# configs/rules.yaml\n- action:\n    id: act_notify\n    type: webhook_notify\n    params:\n      url: \"https://hooks.example.com/loyalty\"\n```\n\n---\n\n## Observability\n\n### Prometheus metrics\n\n| Metric | Type | Labels |\n|--------|------|--------|\n| `ifttt_events_enqueued_total` | Counter | — |\n| `ifttt_events_processed_total` | Counter | — |\n| `ifttt_events_dropped_total` | Counter | — |\n| `ifttt_scenarios_matched_total` | Counter | `scenario_id` |\n| `ifttt_actions_executed_total` | Counter | `action_type`, `status` |\n| `ifttt_event_processing_duration_ms` | Histogram | — |\n| `ifttt_queue_utilization_ratio` | Gauge | — |\n\n### Structured logs (`log/slog`)\n\n```\ntime=2026-02-21T10:30:01.234Z level=INFO msg=\"request\" method=POST path=/v1/events status=200 duration_ms=1\ntime=2026-02-21T10:30:02.100Z level=INFO msg=\"DAG hot-reloaded\" nodes=12\n```\n\n---\n\n## Dependencies\n\n| Module | Purpose |\n|--------|---------|\n| [`gopkg.in/yaml.v3`](https://pkg.go.dev/gopkg.in/yaml.v3) | YAML parsing |\n| [`github.com/fsnotify/fsnotify`](https://pkg.go.dev/github.com/fsnotify/fsnotify) | Config hot-reload |\n| [`github.com/prometheus/client_golang`](https://pkg.go.dev/github.com/prometheus/client_golang) | Metrics |\n| [`github.com/google/uuid`](https://pkg.go.dev/github.com/google/uuid) | Auto-generated event IDs |\n\nZero web frameworks — Go 1.22 `net/http` with method+path routing.\n\n---\n\n## Documentation\n\n| Doc | Contents |\n|-----|----------|\n| [README](README.md) | Setup · API · expression language · extension guide |\n| [TEST.md](TEST.md) | Test inventory · manual end-to-end verification |\n| [DEEPDIVE.md](DEEPDIVE.md) | Architecture · design decisions · performance analysis |\n| [CONTRIBUTING.md](CONTRIBUTING.md) | Fork workflow · conventions · commit style |\n| [CHANGELOG.md](CHANGELOG.md) | Release history |\n| [SECURITY.md](SECURITY.md) | Vulnerability reporting |\n\n---\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md). Bug reports and feature requests go through [GitHub Issues](https://github.com/gyaan/fluxflow/issues).\n\n## License\n\n[MIT](LICENSE) © 2026 Gyaneshwar Pardhi\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgyaan%2Ffluxflow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgyaan%2Ffluxflow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgyaan%2Ffluxflow/lists"}