{"id":13813546,"url":"https://github.com/aperturerobotics/controllerbus","last_synced_at":"2025-04-09T20:11:31.911Z","repository":{"id":56815663,"uuid":"140508365","full_name":"aperturerobotics/controllerbus","owner":"aperturerobotics","description":"Modular applications in Go","archived":false,"fork":false,"pushed_at":"2025-04-09T02:15:22.000Z","size":7374,"stargazers_count":57,"open_issues_count":2,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-09T20:11:26.804Z","etag":null,"topics":["controllers","dynamic-configuration","go","plugin","webassembly","yaml"],"latest_commit_sha":null,"homepage":"","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/aperturerobotics.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}},"created_at":"2018-07-11T02:00:13.000Z","updated_at":"2025-04-04T22:40:51.000Z","dependencies_parsed_at":"2023-12-03T05:22:25.247Z","dependency_job_id":"d9f8c0ee-1a4c-4742-9094-c6f42d3da5b0","html_url":"https://github.com/aperturerobotics/controllerbus","commit_stats":{"total_commits":746,"total_committers":5,"mean_commits":149.2,"dds":"0.43833780160857905","last_synced_commit":"b922cf5bcb9d113e06b157dc8697d2c80878de49"},"previous_names":[],"tags_count":219,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aperturerobotics%2Fcontrollerbus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aperturerobotics%2Fcontrollerbus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aperturerobotics%2Fcontrollerbus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aperturerobotics%2Fcontrollerbus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aperturerobotics","download_url":"https://codeload.github.com/aperturerobotics/controllerbus/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248103872,"owners_count":21048245,"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":["controllers","dynamic-configuration","go","plugin","webassembly","yaml"],"created_at":"2024-08-04T04:01:21.031Z","updated_at":"2025-04-09T20:11:31.888Z","avatar_url":"https://github.com/aperturerobotics.png","language":"Go","readme":"![Controller Bus](./doc/img/controller-bus-logo.png)\n\n## Introduction\n\n[![GoDoc Widget]][GoDoc] [![Go Report Card Widget]][Go Report Card]\n\n[GoDoc]: https://godoc.org/github.com/aperturerobotics/controllerbus\n[GoDoc Widget]: https://godoc.org/github.com/aperturerobotics/controllerbus?status.svg\n[Go Report Card Widget]: https://goreportcard.com/badge/github.com/aperturerobotics/controllerbus\n[Go Report Card]: https://goreportcard.com/report/github.com/aperturerobotics/controllerbus\n\n**ControllerBus** is a framework for **communicating control loops**:\n\n - **Configurable**: flexible self-documenting config with Protobuf and YAML.\n - **Cross-platform**: supports web browsers, servers, desktop, mobile, ...\n - **Hot-loadable**: plugins and IPC dynamically add controllers at runtime.\n - **Modular**: easily combine together application components w/o glue code.\n - **Declarative**: de-duplicated declarative requests between controllers.\n\nThe primary concepts are:\n\n - **Config**: configuration for a controller or process.\n - **Controller**: goroutine which can create \u0026 handle Directives.\n - **Directive**: a cross-controller request or declaration of target state.\n - **Bus**: communication channel between running Controllers.\n - **Factory**: constructor for controller and configuration objects.\n\nController Bus provides a pattern for structuring modular Go projects.\n\n## Examples\n\n[![Support Server](https://img.shields.io/discord/803825858599059487.svg?label=Discord\u0026logo=Discord\u0026colorB=7289da\u0026style=for-the-badge)](https://discord.gg/ZAZSt8CweP)\n\n[![asciicast](https://asciinema.org/a/418275.svg)](https://asciinema.org/a/418275)\n\nBasic demo of the controllerbus daemon and ConfigSet format:\n\n```sh\ncd ./cmd/controllerbus\ngo build -v\n./controllerbus daemon\n```\n\nThis will load `controllerbus_daemon.yaml` and execute the boilerplate demo:\n\n```\nadded directive                               directive=\"LoadControllerWithConfig\u003cconfig-id=controllerbus/configset\u003e\"\nadded directive                               directive=\"ExecController\u003cconfig-id=controllerbus/configset\"\nadded directive                               directive=\"LoadConfigConstructorByID\u003cconfig-id=controllerbus/example/boilerplate\u003e\"\nstarting controller                           controller=controllerbus/configset\nadded directive                               directive=\"ApplyConfigSet\u003ccontroller-keys=boilerplate-example-0@1\u003e\"\nadded directive                               directive=\"LoadControllerWithConfig\u003cconfig-id=controllerbus/bus/api\u003e\"\nremoved directive                             directive=\"LoadConfigConstructorByID\u003cconfig-id=controllerbus/example/boilerplate\u003e\"\nadded directive                               directive=\"ExecController\u003cconfig-id=controllerbus/bus/api\u003e\"\nexecuting controller                          config-key=boilerplate-example-0 controller=controllerbus/configset\nstarting controller                           controller=controllerbus/bus/api\ngrpc api listening on: :5110                 \nadded directive                               directive=\"LoadControllerWithConfig\u003cconfig-id=controllerbus/example/boilerplate\u003e\"\nadded directive                               directive=\"ExecController\u003cconfig-id=controllerbus/example/boilerplate\u003e\"\nstarting controller                           controller=controllerbus/example/boilerplate\nhello from boilerplate controller 1: hello world  controller=controllerbus/example/boilerplate\ncontroller exited normally                    controller=controllerbus/example/boilerplate exec-dur=\"31.053µs\"\n```\n\n### ConfigSet\n\n**ConfigSet** is a key/value set of controller configurations to load.\n\nThe following is an example ConfigSet in YAML format for a program:\n\n```yaml\nexample-1:\n  # configuration object\n  config:\n    exampleField: \"Hello world!\"\n  # ID of the configuration type\n  id: controllerbus/example/boilerplate\n  # rev # for overriding previous configs\n  rev: 1\n```\n\nIn this case, `example-1` is the ID of the controller. If multiple ConfigSet are\napplied with the same ID, the latest rev wins. The ConfigSet controller\nwill automatically start and stop controllers as ConfigSets are changed.\n\n### How does it work?\n\nControllers are executed by attaching them to a Bus. When attaching to a Bus,\nall ongoing Directives are passed to the new Controller. The Controllers can\nreturn Resolver objects to resolve result objects for Directives.\n\nThere are multiple ways to start a Controller:\n\n - `AddController`: with the Go API: construct \u0026 add the controller.\n - `AddDirective` -\u003e `ExecControllerWithConfig`: with a directive.\n - yaml/json: resolving human-readable configuration to Config objects.\n\nConfig objects are Protobuf messages with attached validation functions. They\ncan be hand written in YAML and parsed to Protobuf or be created as Go objects.\n\n### Protobuf Configuration\n\nThe [boilerplate](./example/boilerplate/controller/config.proto) example has the\nfollowing configuration proto:\n\n```protobuf\n// Config is the boilerplate configuration.\nmessage Config {\n  // ExampleField is an example configuration field.\n  string example_field = 1;\n}\n```\n\nThis is an example YAML configuration for this controller:\n\n```yaml\nexampleField: \"Hello world!\"\n```\n\nWith the Go API, we can use the **LoadControllerWithConfig** directive to\nexecute the controller with a configuration object:\n\n```go\n\tbus.ExecOneOff(\n\t\tctx,\n\t\tcb,\n\t\tresolver.NewLoadControllerWithConfig(\u0026boilerplate_controller.Config{\n\t\t\tExampleField: \"Hello World!\",\n\t\t}),\n\t\tnil,\n\t)\n```\n\n## Daemon and API\n\nThe [example daemon](./cmd/controllerbus) has an associated client and CLI for\nthe [Bus API](./bus/api), for example:\n\n```sh\n$ controllerbus client exec -f controllerbus_daemon.yaml\n```\n\n```json\n  {\n    \"controllerInfo\": {\n      \"version\": \"0.0.1\",\n      \"id\": \"controllerbus/example/boilerplate\"\n    },\n    \"status\": \"ControllerStatus_RUNNING\",\n    \"id\": \"boilerplate-example-0\"\n  }\n```\n\nThe bus service has the following API:\n\n```protobuf\n// ControllerBusService is a generic controller bus lookup api.\nservice ControllerBusService {\n  // GetBusInfo requests information about the controller bus.\n  rpc GetBusInfo(GetBusInfoRequest) returns (GetBusInfoResponse) {}\n  // ExecController executes a controller configuration on the bus.\n  rpc ExecController(controller.exec.ExecControllerRequest) returns (stream controller.exec.ExecControllerResponse) {}\n}\n```\n\nThe RPC API is itself implemented as a controller, which can be configured:\n\n```yaml\ngrpc-api:\n  config:\n    listenAddr: \":5000\"\n    busApiConfig:\n      enableExecController: true\n  id: controllerbus/bus/api\n  rev: 1\n```\n\nFor security, the default value of `enableExecController` is `false` to disallow\nexecuting controllers via the API.\n\nThe structure under `cmd/controllerbus` and `example/boilerplate` are examples\nwhich are intended to be copied to other projects, which reference the core\n`controllerbus` controllers. A minimal program is as follows:\n\n```go\n\tctx := context.Background()\n\tlog := logrus.New()\n\tlog.SetLevel(logrus.DebugLevel)\n\tle := logrus.NewEntry(log)\n\n\tb, sr, err := core.NewCoreBus(ctx, le)\n\tif err != nil {\n\t\tt.Fatal(err.Error())\n\t}\n\tsr.AddFactory(NewFactory(b))\n\n\texecDir := resolver.NewLoadControllerWithConfig(\u0026Config{\n\t\tExampleField: \"testing\",\n\t})\n\t_, ctrlRef, err := bus.ExecOneOff(ctx, b, execDir, nil)\n\tif err != nil {\n\t\tt.Fatal(err.Error())\n\t}\n\tdefer ctrlRef.Release()\n```\n\nThis provides logging, context cancelation. A single Factory is attached which\nprovides support for the Config type, (see the boilerplate example).\n\n## Daemon and Client CLIs\n\nPlugins can be bundled together with a set of root configurations into a CLI.\nThis can be used to bundle modules into a daemon and/or client for an\napplication - similar to the [controllerbus cli](./cmd/controllerbus).\n\n## Testing\n\nAn in-memory Bus can be created for testing, an\n[example](./example/boilerplate/controller/controller_test.go) is provided in\nthe boilerplate package.\n\n## Plugins\n\n[![asciicast](https://asciinema.org/a/I4LOCViLwzRlztYc1rytgAxWp.svg)](./example/plugin-demo)\n\n**⚠ Plugins are experimental and not yet feature-complete.**\n\nThe [plugin](./plugin) system and compiler scans a set of Go packages for\nControllerBus factories and bundles them together into a hashed Plugin bundle.\nThe compiler CLI can watch code files for changes and re-build automatically.\nMultiple plugin loaders and binary formats are supported.\n\n```\nUSAGE:\n   controllerbus hot compile - compile packages specified as arguments once\n\nOPTIONS:\n   --build-prefix value           prefix to prepend to import paths, generated on default [$CONTROLLER_BUS_PLUGIN_BUILD_PREFIX]\n   --codegen-dir value            path to directory to create/use for codegen, if empty uses tmpdir [$CONTROLLER_BUS_CODEGEN_DIR]\n   --output PATH, -o PATH         write the output plugin to PATH - accepts {buildHash} [$CONTROLLER_BUS_OUTPUT]\n   --plugin-binary-id value       binary id for the output plugin [$CONTROLLER_BUS_PLUGIN_BINARY_ID]\n   --plugin-binary-version value  binary version for the output plugin, accepts {buildHash} [$CONTROLLER_BUS_PLUGIN_BINARY_VERSION]\n   --no-cleanup                   disable cleaning up the codegen dirs [$CONTROLLER_BUS_NO_CLEANUP]\n   --help, -h                     show help\n```\n\nThe CLI will analyze a list of Go package paths, discover all Factories\navailable in the packages, generate a Go module for importing all of the\nfactories into a single Plugin, and compile that package to a .so library.\n\n## Projects\n\nList of projects known to use Controller Bus:\n\n - [Bifrost]: networking and p2p library + daemon\n\n[Bifrost]: https://github.com/aperturerobotics/bifrost\n\nOpen a PR to add your project to this list!\n\n## Support\n\nPlease open a [GitHub issue] with any questions / issues.\n\n[GitHub issue]: https://github.com/aperturerobotics/controllerbus/issues/new\n\n... or feel free to reach out on [Matrix Chat] or [Discord].\n\n[Discord]: https://discord.gg/ZAZSt8CweP\n[Matrix Chat]: https://matrix.to/#/#aperturerobotics:matrix.org\n\n## License\n\nMIT\n","funding_links":[],"categories":["Go"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faperturerobotics%2Fcontrollerbus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faperturerobotics%2Fcontrollerbus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faperturerobotics%2Fcontrollerbus/lists"}