{"id":13614089,"url":"https://github.com/antonmi/kraken","last_synced_at":"2025-06-20T03:38:24.922Z","repository":{"id":163470524,"uuid":"636601411","full_name":"antonmi/kraken","owner":"antonmi","description":"Flow-based System Orchestration Framework","archived":false,"fork":false,"pushed_at":"2024-05-25T12:34:07.000Z","size":718,"stargazers_count":54,"open_issues_count":1,"forks_count":1,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-04-11T02:11:52.368Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/antonmi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2023-05-05T08:12:05.000Z","updated_at":"2025-01-15T18:43:07.000Z","dependencies_parsed_at":null,"dependency_job_id":"a5b6dfc4-45cb-4352-9145-2b90d6370afe","html_url":"https://github.com/antonmi/kraken","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antonmi%2Fkraken","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antonmi%2Fkraken/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antonmi%2Fkraken/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antonmi%2Fkraken/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/antonmi","download_url":"https://codeload.github.com/antonmi/kraken/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248328160,"owners_count":21085261,"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","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":[],"created_at":"2024-08-01T20:00:56.766Z","updated_at":"2025-04-11T02:11:57.642Z","avatar_url":"https://github.com/antonmi.png","language":"Elixir","funding_links":[],"categories":["Elixir"],"sub_categories":[],"readme":"# Kraken\n\n[![Hex.pm](https://img.shields.io/hexpm/v/kraken.svg?style=flat-square)](https://hex.pm/packages/kraken)\n\n\u003cimg src=\"images/kraken-logo.jpeg\" \nalt=\"Happy Kraken\" \nwidth=500px\u003e\n\n**Flow-based System Orchestration Framework**\n\nKraken is a general framework for the orchestration of software systems.\n\nIt suggests the flow-based programming approach for organizing the interaction between the parts of the system (specific services).\nIt means that every interaction with the system is presented as an “input event” - the data structure that contains all the necessary information about the interaction.\nThe event then goes through a predefined chain of “components” (“pipelines”), the components call underlying services and modify the event.\nThe final state of the event represents the result of the interaction.\n\nKraken provides a simple declarative JSON DSL for the pipeline definitions as well as for the definitions of the “clients” to underlying services.\n\nWhen thinking about layered architecture (infrastructure, domain, application, and interface layer), Kraken is like Kubernetes for the application layer of the system.\nWhile K8s provides a declarative approach for defining the infrastructure layer (the one below the domain layer), Kraken does the same for the application layer of the system (orchestration layer).\n\nIn terms of the building blocks for the pipelines, Kraken has the same set of components as the [ALF framework](https://github.com/antonmi/ALF). \n\nKraken depends on ALF and under the hood the JSON-DSL definitions are being compiled into the ALF pipelines modules. Read the [ALF Readme](https://github.com/antonmi/ALF/blob/main/README.md) to understand how it works on the Elixir level.\n\nIn terms of “clients”, Kraken uses the [Octopus](https://github.com/antonmi/octopus) library, which uses the simple “prepare-call-transform” approach for communication with external services. See the Octopus Readme for more details.\n\n## Something to read and watch\n\n[Kraken Flow-based Service Orchestration Framework](https://www.youtube.com/watch?v=-m5iO6YBlCk) - Talk at Elixir Berlin Meetup, June 8, 2023.\n\n[Kraken Flow-based Service Orchestration Framework](https://youtube.com/watch?v=9OLHkhHH5WE\u0026list=PLvL2NEhYV4Ztkk8LyJ42hVBCjv6XRK5Ei\u0026index=49) - Lightning Talk at CodeBEAM Europe, October 19, 2023.\n\n[Kraken Flow-based Service Orchestration Framework](https://betterprogramming.pub/kraken-flow-based-service-orchestration-framework-5f2af5ee3d59) - Medium article.\n\n[Declarative Interface Translation with Octopus](https://anton-mishchuk.medium.com/declarative-interface-translation-with-octopus-7bbf4e2329cf) - Medium article.\n\n## Quick start\nAdd \"kraken\" to your Elixir project.\n\nRun `mix kraken.init`. It creates the \"kraken folder\" in your project with a couple of services, the \"hello\" pipeline, the \"kv_store\" client, simple helper, lambda and routes.\nIt gives you a good scaffold for creating your own functionality.\nThe task also create a couple of tests in the \"test/kraken\" folder.\n\nThe \"hello\" pipeline produces ether \"Hello, #{name}!\" or \"I've already said hello to you, #{name}.\" messages.\n\n\u003cimg src=\"images/hello-pipeline.png\" \nalt=\"Happy Kraken\" \nwidth=500px\u003e\n\nIf you put\n\n```elixir\nconfig :kraken,\n       project_start: true\n```\nto the `config/dev.exs` file, the JSON definitions will be compiled when project starts.\nSo you can run the pipeline in the \"iex\" console.\n```elixir\niex(1)\u003e Kraken.call(%{\"type\" =\u003e \"hello\", \"name\" =\u003e \"Anton\"})\n%{\n  \"greeted\" =\u003e %{\"value\" =\u003e nil},\n  \"message\" =\u003e \"Hello, Anton!\",\n  \"name\" =\u003e \"Anton\",\n  \"type\" =\u003e \"hello\"\n}\n```\nOr using kraken client (mix tasks, see below):\n\nFirst, start the app:\n```shell\nmix run --no-halt\nor\niex -S mix\n```\n```shell\n➜ mix kraken call '{\"type\": \"hello\", \"name\": \"Anton\"}'\n{\n  \"greeted\": {\n    \"value\": null\n  },\n  \"message\": \"Hello, Anton!\",\n  \"name\": \"Anton\",\n  \"type\": \"hello\"\n}\n```\n\n`mix kraken.call '{\"type\": \"hello\", \"name\": \"Anton\"}'` (with dot) will also work.\n\nCheck the generated examples and tests to get the basics!\n\n## More complex example of interaction with APIs \n\nThe GitHub service accessible via JSON API is used for the artificial example.\n\nThe pipeline has to return a chain of followers for the given search query.\n- Input: search “query” and “limit”.\n- First, return the information about the first found user.\n- Then get followers of the user.\n- Select the follower whose username is closest to the current username (Levenshtein distance).\n- Return the information about the “closest” follower.\n- Set the current username to the “closest” follower, and repeat from the “get followers\" step.\n- Return not more than “limit” results.\n\nFor example, for query=\"antonmi\", and limit=5, the chain of the usernames might be:\n```text\nantonmi -\u003e antouanbg -\u003e guangyuzhang -\u003e gamemann -\u003e damaru\n```\n\nThe full code can be found [here](https://github.com/antonmi/kraken_examples/tree/main/apps/github)\n\n### Services definition\nThe service definition for the GitHub API is:\n```json\n{\n  \"name\": \"github\",\n  \"client\": {\n    \"module\": \"OctopusClientHttpFinch\",\n    \"start\": {\n      \"base_url\": \"https://api.github.com\",\n      \"headers\": {\n        \"Accept\": \"application/vnd.github+json\"\n      }\n    }\n  },\n  \"interface\": {\n    \"find_users\": {\n      \"prepare\": {\n        \"method\": \"GET\",\n        \"path\": \"/search/users\",\n        \"params\": {\n          \"q\": \"args['query']\",\n          \"per_page\": \"args['per_page']\"\n        }\n      },\n      \"call\": {\"parse_json_body\": true},\n      \"transform\": {\n        \"usernames\": \"get_in(args, ['body', 'items', Access.all(), 'login'])\"\n      }\n    },\n    \"get_user\": {\n      \"prepare\": {\n        \"method\": \"GET\",\n        \"path\": \"'/users/' \u003c\u003e args['username']\"\n      },\n      \"call\": {\"parse_json_body\": true},\n      \"transform\": {\n        \"username\": \"get_in(args, ['body', 'login'])\",\n        \"name\": \"get_in(args, ['body', 'name'])\",\n        \"company\": \"get_in(args, ['body', 'company'])\",\n        \"location\": \"get_in(args, ['body', 'location'])\",\n        \"followers\": \"get_in(args, ['body', 'followers'])\"\n      }\n    },\n    \"get_followers\": {\n      \"prepare\": {\n        \"method\": \"GET\",\n        \"path\": \"'users/' \u003c\u003e args['username'] \u003c\u003e '/followers'\",\n        \"params\": {\n          \"page\": \"args['page']\",\n          \"per_page\": \"args['per_page']\"\n        }\n      },\n      \"call\": {\"parse_json_body\": true},\n      \"transform\": {\n        \"followers\": \"get_in(args, ['body', Access.all(), 'login'])\"\n      }\n    }\n  }\n}\n```\nThe \"client\" section defines the transport-level module for HTTP communication - \"OctopusClientHttpFinch\".\nIt's a very simple [library](https://hex.pm/packages/octopus_client_http_finch) built on top of the Elixir Finch HTTP client.\n\nThe interface section defines three functions: \"find_users\", \"get_user\", \"get_followers\". Each function takes a JSON object as input.\n\nThe \"prepare\" step prepares data for the call.\n\nThe \"call\" step can have some additional options that will be passed to the low-level client (e.g. {\"parse_json_body\": true})\n\nAnd the \"transform\" step finally gets the necessary data from the API response.\n\nOne may see a bit of Elixir code here. Only simple \"access\" functions are available.\nAlso, it is possible to define custom helpers for more complex data extraction.\nSee the [Octopus](https://github.com/antonmi/octopus) page and code.\n\nThere will be also one lambda-like service for the calculation of the Levenshtein distance.\nOctopus (and Kraken) allows one to define a service from an Elixir module.\nHere is the JSON definition:\n```json\n{\n  \"name\": \"levenshtein\",\n  \"client\": {\n    \"module\": \"octopus.lambda\",\n    \"start\": {\n      \"code\": [\n        \"defmodule Distance do\",\n        \"def closest(%{\\\"name\\\" =\u003e name, \\\"names\\\" =\u003e names}) do\",\n        \"closest = Enum.min_by(names, \u0026Levenshtein.distance(\u00261, name))\",\n        \"%{\\\"closest\\\" =\u003e closest}\",\n        \"end\",\n        \"end\"\n      ]\n    }\n  },\n  \"interface\": {\n    \"closest\": {\n      \"input\": {\n        \"name\": {\"type\": \"string\"},\n        \"names\": {\"type\": \"array\"}\n      },\n      \"call\": {\n        \"module\": \"Distance\",\n        \"function\": \"closest\"\n      },\n      \"output\": {\n        \"closest\": {\"type\": \"string\"}\n      }\n    }\n  }\n}\n```\nOne can see the \"code\" in the definition.\nThere are no \"prepare\" and \"transform\" steps, since the \"closest\" function already accepts the JSON-like Elixir map, and returns a similar data structure. \n\nAlso, there are \"input\" and \"output\" in the interface definition. These are optional steps for data validations.\nOctopus uses the [ex_json_schema](https://github.com/jonasschmidt/ex_json_schema) library for optional validations of input and output.\n\nThat's it. After submitting these definitions to the Kraken-based agent, and starting the services, \n```text\nPOST /services/define github-definition\nPOST /services/define levenshtein-definition\nPOST /services/start/github\nPOST /services/start/levenshtein\n```\none call services like:\n```text\nPOST /services/call/github/find_users {\"query\": \"antonmi\"}\nPOST /services/call/levenshtein/closest {\"name\": \"anton\", \"names\": [\"bread\", \"baton\", \"salat\"]}\n```\n\n### Pipeline definition\nThe ALF flow diagram for the solution might be:\n![Solution](images/github-solution.png \"Solution\")\n\nThe pipeline definition is:\n```json\n{\n  \"name\": \"stream-similar-followers\",\n  \"components\": [\n    {\n      \"type\": \"stage\",\n      \"name\": \"init\",\n      \"transform\": {\"counter\": 0, \"limit\": \"args['limit'] || 10\"}\n    },\n    {\n      \"type\": \"stage\",\n      \"name\": \"search\",\n      \"service\": {\"name\": \"github\", \"function\": \"find_users\"},\n      \"prepare\": {\"query\":  \"args['query']\", \"per_page\": 1},\n      \"transform\": {\"username\":  \"get_in(args, ['usernames', Access.at(0)])\"}\n    },\n    {\n      \"type\": \"goto-point\",\n      \"name\": \"repeat\"\n    },\n    {\n      \"type\": \"stage\",\n      \"name\": \"get-user\",\n      \"service\": {\"name\": \"github\", \"function\": \"get_user\"},\n      \"prepare\": {\"username\":  \"args['username']\"},\n      \"transform\": {\"user_data\":  \"args\"}\n    },\n    {\n      \"type\": \"stage\",\n      \"name\": \"inc-counter\",\n      \"transform\": {\"counter\":  \"args['counter'] + 1\"}\n    },\n    {\n      \"type\": \"composer\",\n      \"name\": \"clone-event\",\n      \"memo\": null,\n      \"service\": {\n        \"name\": \"clone-service\",\n        \"function\": \"clone\"\n      },\n      \"prepare\": {\n        \"event\": \"args\",\n        \"memo\": \"memo\"\n      },\n      \"compose\": {\n        \"events\": \"args['events']\",\n        \"memo\": \"args['memo']\"\n      }\n    },\n    {\n      \"type\": \"switch\",\n      \"condition\": \"if args['report'], do: \\\"output\\\", else: \\\"next-steps\\\"\",\n      \"branches\": {\n        \"output\": [\n          {\n            \"type\": \"stage\",\n            \"name\": \"print\",\n            \"service\": {\"name\": \"io-inspect\", \"function\": \"print\"},\n            \"prepare\": {\"value\":  \"args\"}\n          }\n        ],\n        \"next-steps\": [\n          {\n            \"type\": \"stage\",\n            \"name\": \"get-followers\",\n            \"service\": {\"name\": \"github\", \"function\": \"get_followers\"},\n            \"prepare\": {\"username\":  \"args['username']\"},\n            \"transform\": {\"followers\":  \"args['followers']\"}\n          },\n          {\n            \"type\": \"switch\",\n            \"prepare\": {\n              \"count\": \"length(args['followers'])\",\n              \"counter\": \"args['counter']\",\n              \"limit\": \"args['limit']\"\n            },\n            \"condition\": \"if args['counter'] \u003e 0 \u0026\u0026 args['counter'] \u003c args['limit'], do: \\\"continue\\\", else: \\\"done\\\"\",\n            \"branches\": {\n              \"continue\": [\n                {\n                  \"type\": \"stage\",\n                  \"name\": \"find-closest\",\n                  \"service\": {\"name\": \"levenshtein\", \"function\": \"closest\"},\n                  \"prepare\": {\"name\":  \"args['username']\", \"names\":  \"args['followers']\"},\n                  \"transform\": {\"closest\":  \"args['closest']\"}\n                },\n                {\n                  \"type\": \"stage\",\n                  \"name\": \"reassign-vars\",\n                  \"transform\": {\"username\": \"args['closest']\", \"followers\": \"hidden\"}\n                },\n                {\n                  \"type\": \"goto\",\n                  \"to\": \"repeat\",\n                  \"condition\": \"true\"\n                }\n              ],\n              \"done\": [{\"type\": \"dead-end\"}]\n            }\n          }\n        ]\n      }\n    }\n  ]\n}\n\n```\nOne can see several components here:\n- \"stage\" - for calling of underlying services.\n- \"composer\" - for creating a copy for output.\n- \"switch\" and \"goto\" for directing the \"event\".\n\nStages have the \"prepare\", \"service\", and \"transform\" sections.\nFor example the \"get-user\" component:\n```json\n{\n  \"type\": \"stage\",\n  \"name\": \"get-user\",\n  \"prepare\": {\"username\":  \"args['username']\"},\n  \"service\": {\"name\": \"github\", \"function\": \"get_user\"},\n  \"transform\": {\"user_data\":  \"args\"}\n}\n```\nIt first gets 'username' from the event, then call the \"get_user\" function on the \"github\" service, \nand finally updates the event puts the event by setting 'user_data'. \n\nThere are also stages that just \"transform\" the event - \"inc-counter\" and \"reassign-vars\".\n\nAfter posting the definition to the Kraken agent, magic happens, and the ALF pipeline appears in the agent code. \n```text\nPOST /pipelines/define pipeline-definition\nPOST /pipelines/start/stream-similar-followers\n```\nThen one can post events to the pipeline and get the results back:\n```text\nPOST /pipelines/call/stream-similar-followers  {\"query\": \"antonmi\", \"limit\": 5}\nor\nPOST /pipelines/stream/stream-similar-followers  {\"query\": \"antonmi\", \"limit\": 5}\nto stream the output events \n```\n\n## More realistic cases\nLet's say you have an online shop system.\n\nThere might be \"user-service\", \"products-service\", \"billing-service\", and \"notification-service\" services.\n\nThere might be a dozen of \"flows\", or \"business-flow\", or \"user-flows\", or \"use-cases\", that are \"pipelines\" in Kraken (ALF) terminology:\n- User registration\n- User login\n- User lists products\n- User orders products\n- etc. and etc.\n\nEach service is deployed to the system and exposes its API.\n\nNow, the job of the \"application engineer\" is to design and build these flows in the system.\n\nFor each \"use-case\", one should: \n- define the \"services\" - uniform mapping to the underlying services API.\n- define the \"event\" that represents the interaction (and its result).\n- define the \"pipeline\" - a chain of components that will transform the event accordingly to the business logic.\n\nFor example, for the \"User orders products\", the \"event\" representing the interaction would be:\n```json\n{\n  \"type\": \"user-orders-a-product\",\n  \"user_id\": \"user-123\",\n  \"product_id\": \"product-456\", \n}\n```\n\nThe corresponding pipeline (simplified):\n\n\u003cimg src=\"images/online-shop.png\" \nalt=\"user-orders-a-product pipeline\" \nwidth=500px\u003e\n\n```json\n{\n  \"name\": \"user-orders-a-product\",\n  \"components\": \n    [\n      {\n          \"name\": \"find-user\",\n          \"service\": {\"name\": \"user-service\", \"function\": \"find\"}\n      },\n      {\n          \"name\": \"find-product\",\n          \"service\": {\"name\": \"product-service\", \"function\": \"find\"}\n      },\n      {\n          \"name\": \"bill-user\",\n          \"service\": {\"name\": \"billing-service\", \"function\": \"bill\"}\n      },\n      {\n          \"name\": \"notify-user\",\n          \"service\": {\"name\": \"notification-service\", \"function\": \"send-email\"}\n      }\n    ]\n}\n```\nSo, the event goes through four components, each component calls underlying services.\nAfter all the transformations, the output would be like:\n```json\n{\n  \"type\": \"user-orders-a-product\",\n  \"success\": \"yes\",\n  \"order_id\": \"789\", \n}\n```\n\nAfter the definitions are ready, one can simply push them to the Kraken-based agent that is deployed to the system.\n\nIt's just a new instance (pod) in the system that will coordinate all the underlying services.\n\n## Philosophy. Or why is it cool?\n### Flow-based programming\nThe approach applies the great engineering idea of an assembly line.\nAn initial workpiece is placed on the conveyor and goes through several processing stages until it becomes a ready-to-use product.\n\nThis flow-based programming is a case of the general \"event-driven\" approach, where business logic is triggered by data.\nThe main distinction are:\n- The topology of the \"flow\" is defined explicitly.    \n- A single event represents the result of the interaction with the particular use-case in the software system.\n\nThis provides extraordinary observability and, therefore, understandability of the defined logic:\n- One can easily read \"pipelines\" (both in code and diagrams).\n- One can \"sit\" on an event and easily track its changes when passing through components in a pipeline.\n- One can \"sit\" on a component and observe transformations of different events passing through.\n\n### Elixir GenStages, scaling, and monitoring\nUnder the hood, the ALF runs all components' code inside Elixir GenServers. It means that all the components work in parallel. \nWhen one pushes the stream of events to the pipeline, one component works with an n-th event, and the previous one work on (n-1)-th.\n\nAlso, ALF (and therefore Kraken) allows having several copies of a component, which adds additional (and configurable) horizontal parallelism.\n\nALF uses the Elixir/Erlang [telemetry](https://github.com/beam-telemetry/telemetry) library, so it's easy to track the event transformation on each step.\n\nSee the [ALF-monitor](https://github.com/antonmi/alf_monitor) project and video there.\n\n### Declarative JSON DSL\nWhat can be simpler than JSON?\n\nKraken uses it for services and pipelines definitions, and events are also just JSON objects.\n\nSo, Kraken provides a very simple DLS for modeling the whole application (orchestration) layer for a software system.\n\nMoreover, services definitions can be considered as a specification for the underlying service:\n- services \"names\" and \"interface functions\" (together with input/output specifications) define a \"language\" for communicating with underlying domain services.\n- the \"specifications\" can (even must) be used by the engineers that work with domain-level services.\n\nThe pipeline definitions are comprehensible data structures that are consistent with visual representations of the flows defined using ALF diagrams.\n\nThe \"design-, code-, and runtime-uniformity\" buzz-phrase can be used for describing the advantages of the approach:\n- when one designs a \"pipeline\", he/she thinks in terms of a chain of components and draws it on paper using the diagram language.\n- when one codes the pipeline, he/she codes the same structure as was on the paper.\n- and finally, when the program runs, it passes events throw the same chain of components.\n\n### Kraken-agent\nOne can for sure compile all the definitions in a build time and then upload the \"orchestrator\" into the system.\n\nHowever, I see more advantages when the code is uploaded dynamically. So, first one uploads \"an empty\" Kraken-based orchestrator, then uploads all the definitions and starts the pipelines.\n\nIt is similar to the Kubernetes approach to creating an infrastructure layer of the system.\n\nThe code then resides separately from the \"orchestrator\" and can be uploaded using a \"client\", something like `kraken apply .` (similar to `kubectl apply .`).\n\nIt will allow one to dynamically add new services and pipelines (or new versions of them).\n\nIt also opens huge opportunities for developing (prototyping) pipelines on existing systems (better its development-purpose copies).\n\nAnd also an IDE-like client can be implemented, providing a low-code platform for the orchestration layer.  \n\n## Kraken client\nThere are mix tasks for interaction with the application:\n#### Services\n```text\nmix kraken.services\nmix kraken.services define :definition | :file | :path\n\nmix kraken.services status :name\nmix kraken.services definition :name\nmix kraken.services state :name  \nmix kraken.services start :name\nmix kraken.services stop :name \nmix kraken.services delete :name \nmix kraken.services call :name :function :event  \n```\n#### Pipelines\n```text\nmix kraken.pipelines\nmix kraken.pipelines define :definition\nmix kraken.pipelines status :name \nmix kraken.pipelines definition :name\nmix kraken.pipelines start :name\nmix kraken.pipelines stop :name \nmix kraken.pipelines delete :name \nmix kraken.pipelines call :name :event(s)\nmix kraken.pipelines cast :name :event(s)\nmix kraken.pipelines stream :name :events\n```\n#### Routes\n```text\nmix kraken.routes\nmix kraken.routes define :definition\n```\n#### Events\n```text\nmix kraken call :event(s)\nmix kraken cast :event(s)\nmix kraken stream :event(s)\n# also works with dot\nmix kraken.call :event(s)\nmix kraken.cast :event(s)\nmix kraken.stream :event(s)\n```\n\n\n\n## More technical details are below\n\n### Components\nSee [ALF components overview](https://github.com/antonmi/ALF#components-overview) for more details.\n\n| image                                                                         | type     | required attributes                                                        |\n|-------------------------------------------------------------------------------|----------|----------------------------------------------------------------------------|\n| \u003cimg src=\"images/stage.png\" width=100px\u003e                                      | stage    | it can be empty or have only \"transform\" section                           |\n| \u003cimg src=\"images/composer.png\" width=100px\u003e                                   | composer | \"service\", \"compose\" with \"events\" and \"memo\"                              |\n| \u003cimg src=\"images/switch.png\" width=100px\u003e                                     | switch   | \"condition\" - code, and \"branches\" - list of components                    | \n| \u003cimg src=\"images/done.png\" width=100px\u003e                                       | done     | \"condition\" - code, \"true\" by default                                      |\n| \u003cimg src=\"images/goto.png\" width=100px\u003e                                       | goto     | \"to\" - the name of the \"goto-point\", \"condition\" - code, \"true\" by default |\n| \u003cimg src=\"images/goto-point.png\" width=100px\u003e                                 | goto-point | \"name\" - the \"goto\" refers to the name                                     |\n| \u003cimg src=\"images/dead-end.png\" width=100px\u003e                                   | dead-end | nothing                                                                    |\n| \u003cimg src=\"images/plug.png\" width=50px\u003e \u003cimg src=\"images/unplug.png\" width=50px\u003e | plug     | \"pipeline\"                                                                 |\n\n\n### API\n\nSee [router.ex](lib/kraken/api/router.ex)\n\n#### Services\n```text\nPOST /services/define\nGET /services\nGET /services/status/:name\nGET /services/definition/:name\nGET /services/state/:name  \nPOST /services/start/:name\nPOST /services/stop/:name \nPOST /services/delete/:name \nPOST /services/call/:name/:function \n```\n#### Pipelines\n```text\nPOST /pipelines/define \nGET /pipelines \nGET /pipelines/status/:name \nGET /pipelines/definition/:name\nPOST /pipelines/start/:name\nPOST /pipelines/stop/:name \nPOST /pipelines/delete/:name \nPOST /pipelines/call/:name \nPOST /pipelines/cast/:name \nPOST /pipelines/stream/:name \n```\n#### Routes\n```text\nPOST /routes/define\nGET /routes\n```\n#### Events\n```text\nPOST /call\nPOST /cast\nPOST /stream\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fantonmi%2Fkraken","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fantonmi%2Fkraken","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fantonmi%2Fkraken/lists"}