{"id":13543320,"url":"https://github.com/dyrkin/fsm","last_synced_at":"2026-01-12T06:30:05.906Z","repository":{"id":57489908,"uuid":"158589282","full_name":"dyrkin/fsm","owner":"dyrkin","description":"Finite State Machine for Go inspired by Akka FSM","archived":false,"fork":false,"pushed_at":"2020-01-22T09:10:01.000Z","size":10,"stargazers_count":62,"open_issues_count":0,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-12-17T14:50:58.185Z","etag":null,"topics":["akka-fsm","finite-state-machine","fsm","go","golang"],"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/dyrkin.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":"2018-11-21T18:17:32.000Z","updated_at":"2025-08-23T08:47:53.000Z","dependencies_parsed_at":"2022-08-29T19:31:26.935Z","dependency_job_id":null,"html_url":"https://github.com/dyrkin/fsm","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/dyrkin/fsm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dyrkin%2Ffsm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dyrkin%2Ffsm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dyrkin%2Ffsm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dyrkin%2Ffsm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dyrkin","download_url":"https://codeload.github.com/dyrkin/fsm/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dyrkin%2Ffsm/sbom","scorecard":{"id":362490,"data":{"date":"2025-08-11","repo":{"name":"github.com/dyrkin/fsm","commit":"a8e19481365d05eada18e2f4f6513356d7c09637"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":0,"reason":"Found 0/11 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}}]},"last_synced_at":"2025-08-18T11:08:58.409Z","repository_id":57489908,"created_at":"2025-08-18T11:08:58.409Z","updated_at":"2025-08-18T11:08:58.409Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28336316,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-12T06:09:07.588Z","status":"ssl_error","status_checked_at":"2026-01-12T06:05:18.301Z","response_time":98,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: 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":["akka-fsm","finite-state-machine","fsm","go","golang"],"created_at":"2024-08-01T11:00:29.697Z","updated_at":"2026-01-12T06:30:05.885Z","avatar_url":"https://github.com/dyrkin.png","language":"Go","funding_links":[],"categories":["Go","Libraries"],"sub_categories":["Go"],"readme":"# Finite State Machine for Go\n\n## Overview\n\nThe FSM (Finite State Machine) is best described in the [Erlang design principles](http://www.erlang.org/documentation/doc-4.8.2/doc/design_principles/fsm.html)\n\nA FSM can be described as a set of relations of the form:\n\u003e State(S) x Event(E) -\u003e Actions (A), State(S')\n\nThese relations are interpreted as meaning:\n\u003e If we are in state S and the event E occurs, we should perform the actions A and make a transition to the state S'.\n\n## A Simple Example\n\nThe code is taken from the article [Akka Finite State Machine (FSM) and At Most Once Semantics](http://cloudmark.github.io/FSM/)\n\n```go\nimport (\n\t\"fmt\"\n\t\"github.com/dyrkin/fsm\"\n)\n\n//states\nconst InitialState = \"Initial\"\nconst AwaitFromState = \"AwaitFrom\"\nconst AwaitToState = \"AwaitTo\"\nconst DoneState = \"Done\"\n\n//messages\ntype Transfer struct {\n\tsource chan int\n\ttarget chan int\n\tamount int\n}\n\nconst Done = \"Done\"\nconst Failed = \"Failed\"\n\n//data\ntype WireTransferData struct {\n\tsource chan int\n\ttarget chan int\n\tamount int\n\tclient *fsm.FSM\n}\n\nfunc newWireTransfer(transferred chan bool) *fsm.FSM {\n\twt := fsm.NewFSM()\n\n\twt.StartWith(InitialState, nil)\n\n\twt.When(InitialState)(\n\t\tfunc(event *fsm.Event) *fsm.NextState {\n\t\t\ttransfer, transferOk := event.Message.(*Transfer)\n\t\t\tif transferOk \u0026\u0026 event.Data == nil {\n\t\t\t\ttransfer.source \u003c- transfer.amount\n\t\t\t\treturn wt.Goto(AwaitFromState).With(\n\t\t\t\t\t\u0026WireTransferData{transfer.source, transfer.target, transfer.amount, wt},\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn wt.DefaultHandler()(event)\n\t\t})\n\n\twt.When(AwaitFromState)(\n\t\tfunc(event *fsm.Event) *fsm.NextState {\n\t\t\tdata, dataOk := event.Data.(*WireTransferData)\n\t\t\tif dataOk {\n\t\t\t\tswitch event.Message {\n\t\t\t\tcase Done:\n\t\t\t\t\tdata.target \u003c- data.amount\n\t\t\t\t\treturn wt.Goto(AwaitToState)\n\t\t\t\tcase Failed:\n\t\t\t\t\tgo data.client.Send(Failed)\n\t\t\t\t\treturn wt.Stay()\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn wt.DefaultHandler()(event)\n\t\t})\n\n\twt.When(AwaitToState)(\n\t\tfunc(event *fsm.Event) *fsm.NextState {\n\t\t\tdata, dataOk := event.Data.(*WireTransferData)\n\t\t\tif dataOk {\n\t\t\t\tswitch event.Message {\n\t\t\t\tcase Done:\n\t\t\t\t\ttransferred \u003c- true\n\t\t\t\t\treturn wt.Stay()\n\t\t\t\tcase Failed:\n\t\t\t\t\tgo data.client.Stay()\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn wt.DefaultHandler()(event)\n\t\t})\n\treturn wt\n}\n```\n\nThe basic strategy is to instantiate FSM and specifying the possible states:\n\n* `NewFSM()` instantiates new FSM\n* `StartWith()` defines the initial state and initial data\n* Then there is one `When(\u003cstate\u003e)(\u003cevent handler fn\u003e)` declaration per state to be handled.\n\nIn this case will start in the Initial state with all values uninitialized. The only type of message which can be received in the Initial state is the initial Transfer request at which point a withdraw amount is sent to the source account and the state machine transitions to the AwaitFrom state.\n\nWhen the system is in the AwaitFrom state the only two messages that can be received are Done or Failure from the source account. If the Done business acknowledgement is received the system will send a deposit amount to the target account and transition to the AwaitTo state.\n\nWhen the system is in the AwaitTo state the only two messages that can be received are the Done or Failure from the target account.\n\nTo run the code above you can use the following code:\n\n```go\nfunc main() {\n\n\ttransferred := make(chan bool)\n\n\twireTransfer := newWireTransfer(transferred)\n\n\ttransfer := \u0026Transfer{\n\t\tsource: make(chan int),\n\t\ttarget: make(chan int),\n\t\tamount: 30,\n\t}\n\n\tsource := func() {\n\t\twithdrawAmount := \u003c-transfer.source\n\t\tfmt.Printf(\"Withdrawn from source account: %d\\n\", withdrawAmount)\n\t\twireTransfer.Send(Done)\n\t}\n\n\ttarget := func() {\n\t\ttopupAmount := \u003c-transfer.target\n\t\tfmt.Printf(\"ToppedUp target account: %d\\n\", topupAmount)\n\t\twireTransfer.Send(Done)\n\t}\n\n\tgo source()\n\tgo target()\n\n\tgo wireTransfer.Send(transfer)\n\n\tif done := \u003c-transferred; !done {\n\t\tpanic(\"Something went wrong\")\n\t}\n\n\tfmt.Println(\"DONE\")\n}\n```\n\nIt will produce the following output:\n\n\u003e Withdrawn from source account: 30  \nToppedUp target account: 30  \nDONE","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdyrkin%2Ffsm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdyrkin%2Ffsm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdyrkin%2Ffsm/lists"}