{"id":34222674,"url":"https://github.com/abramisola/wave","last_synced_at":"2026-05-26T21:31:06.163Z","repository":{"id":57527076,"uuid":"89411429","full_name":"abramisola/wave","owner":"abramisola","description":"A Go Programming Language Feature Flagging Library","archived":false,"fork":false,"pushed_at":"2017-04-27T04:30:23.000Z","size":33,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-12-22T08:52:17.776Z","etag":null,"topics":["authorization","feature-flags","go","golang","golang-library"],"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/abramisola.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}},"created_at":"2017-04-25T22:09:11.000Z","updated_at":"2021-03-04T06:25:23.000Z","dependencies_parsed_at":"2022-09-07T02:10:28.710Z","dependency_job_id":null,"html_url":"https://github.com/abramisola/wave","commit_stats":null,"previous_names":["abramisola/wave","aisola/wave"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/abramisola/wave","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abramisola%2Fwave","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abramisola%2Fwave/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abramisola%2Fwave/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abramisola%2Fwave/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/abramisola","download_url":"https://codeload.github.com/abramisola/wave/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abramisola%2Fwave/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27757811,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-15T02:00:09.782Z","response_time":96,"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":["authorization","feature-flags","go","golang","golang-library"],"created_at":"2025-12-15T23:48:46.399Z","updated_at":"2025-12-15T23:48:47.047Z","avatar_url":"https://github.com/abramisola.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"Wave\n====\n\nGo programming language feature flagging\n\n[![GoDoc Widget](https://godoc.org/github.com/aisola/wave?status.svg)](https://godoc.org/github.com/aisola/wave)\n[![Build Status](https://travis-ci.org/aisola/wave.svg?branch=master)](https://travis-ci.org/aisola/wave)\n[![Coverage Status](https://coveralls.io/repos/github/aisola/wave/badge.svg?branch=master)](https://coveralls.io/github/aisola/wave?branch=master)\n[![Code Climate](https://codeclimate.com/github/aisola/wave/badges/gpa.svg)](https://codeclimate.com/github/aisola/wave)\n[![Go Report Card](https://goreportcard.com/badge/github.com/aisola/wave)](https://goreportcard.com/report/github.com/aisola/wave)\n[![MIT license](http://img.shields.io/badge/license-MIT-brightgreen.svg)](http://opensource.org/licenses/MIT)\n\nInspired by the [pyrollout](https://github.com/brechin/pyrollout) python\npackage by [Jason Brechin](https://github.com/brechin). I've put no genius\ninto this. I've just ported it and made it slightly more go idiomatic.\n\nI've renamed it from rollout to wave simply because I am lazy and do not want\nto type `rollout` so many times in my go code. Wave is shorter and ebbs and\nflows like a software rollout too.\n\n## Installation \u0026 Tests\n\nUsing good ol' `go get`:\n\n```bash\ngo get -u -v github.com/aisola/wave\ngo test github.com/aisola/wave/...\n```\n\nUsing the new fangled `dep`:\n\n```bash\ndep ensure -update github.com/aisola/wave\ngo test github.com/aisola/wave/...\n```\n\n## Typical Usage\n\nWhile wave allows you to create and manage your own wave instances, the typical\nuser of wave will simply underscore-import a backend and use the default instance.\nHere, since we are not underscore-importing ay different backend, wave uses\nin-memory storage.\n\n```go\n// ...\n\nimport (\n       \"github.com/aisola/wave\"\n)\n\nfunc main() {\n     // ...\n}\n```\n\nIf you want undefined features grant access to all users, you can do that by\nmarking the default `Wave.UndefinedAccess` field as `true`.\n\n```go\nwave.Default.UndefinedAccess = true\n```\n\nNow add features:\n\n```go\n// ...\n\n// Open to all by using the special group 'ALL'\nwave.AddFeature(wave.NewFeatureGroups(\"feature_for_all\", wave.ALL))\n\n// Open to select groups\nwave.AddFeature(wave.NewFeatureGroups(\"feature_for_groups\", []string{\"vip\", \"early-adopter\"}))\n\n// Open to specific user(s), by user ID\nwave.AddFeature(wave.NewFeatureUsers(\"feature_for_users\", []string{\"123\", \"456\", \"789\"}))\n\n// ...\n```\n\nCheck access to features:\n\n```go\n// ...\n\nfunc UntestedFeature(user wave.User) bool {\n     // Because this feature was not defined, access will always be denied, unless you've\n     // set Wave.UndefinedAccess as true.\n     if !wave.Can(user, \"use_untested_feature\") {\n     \t  return false\n     }\n\n     DoSomethingCool()\n\n     return true\n}\n\n\nfunc FooHandler(w http.ResponseWriter, r *http.Request) {\n     // The user type that we get from the request context is one passed in by\n     // an authentication middleware of sorts. This user object should implement\n     // the wave.User interface.\n     ctx := r.Context()\n     user := ctx.Value(\"user\").(*User)\n\n     if !wave.Can(user, \"feature_for_users\") {\n     \t  http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)\n\t  return\n     }\n\n     DoSomethingElseCoolHere(w)\n}\n\n// ...\n```\n\n## Creating multiple Wave instances\n\nIn some cases, you may want to create and use more than one instance of wave in\nyour application. For instance, if you have two different APIs serving out of\nthe same binary.\n\n```go\nimport (\n       \"github.com/aisola/wave\"\n       _ \"github.com/some/wave/backend\"\n)\n// ...\n\napi1 := wave.NewWave(\"backend\")\napi2 := wave.NewWave(\"backend\")\n\nif err := api1.Open(\"backend://username:password@127.0.0.1:8000/api1\"); err != nil {\n   log.Fatalf(\"Could not open up wave backend for api1, %s\", err)\n}\ndefer api1.Close()\n\nif err := api2.Open(\"backend://username:password@127.0.0.1:8000/api2\"); err != nil {\n   log.Fatalf(\"Could not open up wave backend for api2, %s\", err)\n}\ndefer api2.Close()\n\n// Open to all by using the special group 'ALL'\napi1.AddFeature(wave.NewFeatureGroups(\"feature_for_all\", wave.ALL))\n\n// Open to admins\napi2.AddFeature(wave.NewFeatureGroups(\"feature_for_admins\", []string{\"admins\"}))\n\n// ...\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabramisola%2Fwave","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fabramisola%2Fwave","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabramisola%2Fwave/lists"}