Tools for unifying system configuration

25e8387 upload.sh: fix domain

19 days ago

New git repository added

19 days ago


metasetterCore is a library of the core functionality of Metasetter, a utility to help you manage your system configuration in one place.

#System description

Metasetter provides a consistent interface to read and configure the state of a computer. The configuration of each component is managed through a provider, which statelessly represents its configuration and provides an interface to update it. Providers can be organised into provider trees, allowing configuration changes to be grouped into idempotent operations. Clients can be written to interface between users and the providers.

#Use cases

Metasetter is useful any time a program or user needs to view or change to the computer's state. By providing access to the state behind a consistent interface, programs no longer need to be concerned with the format of the configuration for a program. With metasetter, the configuration has the same representation whether derived from a config file, environment variables, a database, or something else.

Clients could range from a GUI settings menu for changing the system's state (e.g. connected networks, hostname, etc.), a toolbar showing data such as number of unread emails, or a wizard which transparently describes the operations requried to bring the system into a desired state.



[x] Build the elixir script provider

[x] Implement global service for filesystem notification, which providers can expect

[x] notify_state_change takes (pid, message) instead of (interface, message) because providers can't know which upstream interfaces they're bound to

[x] Unify all providers to having just one interface

[x] Build the GenericProvider macro, for creating providers with arguments e.g. the protocol verifying provider, the caching provider, etc.

[x] Build the Providers.Literal

[ ] Build the provider tree provider. Includes as a submodule the ProviderChildren provider, which will return a data structure containing just the children requested for a given provider. Building this will require the GenericProvider

[ ] Build the router provider

[ ] Implement children for providers through a provider - each provider has its upstream interface and downstream interface only. The downstream interface will give it its children

[ ] Implement provider settings interface logic. The default handler for settings updating causes the provider to crash and get restarted, thus reloading its interface. The settings get passed directly as the init_arg to init_provider

[ ] Build the simplest possible client: sets up a provider tree with settings from an elixir script provider from a path specified on the command line. This client just displays/updates the state.


[ ] Make the ProviderChildren provider a generic provider, rather than having protocol any()

[ ] metasetterd - talks a simple protocol over a unix domain socket, keeping alive just a single provider tree

[ ] Interface protocols - used to ensure that provider interfaces connected together are compatible

[ ] Implement type system for update commands, so providers can indicate the allowed values, or whether it's read-only. This may or may not directly interact with the interface protocol system.


[ ] Provider tree inspection

[ ] Wizard generation (which may also require a general-purpose wizard provider)


Currently an example can be run by invoking mix run test.exs.

The test suite can be run with mix test, or a specific test e.g. with mix test test/metasetter-providers_file.exs

#To do

Implement protocols for interfaces


Install pre-commit, and run pre-commit install to install the hooks to lint your project and run the tests before committing.

If the linting fails, run mix format.


If available in Hex, the package can be installed by adding metasetter_core to your list of dependencies in mix.exs:

def deps do
    {:metasetter_core, "~> 0.1.0"}

Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/metasetter_core.


#Why do providers have only one upstream interface?

Although having multiple upstream interfaces might seem convenient, the cost-benefit analysis on the code doesn't work out. Multiple upstream interfaces only provides a small advantage in a single case - where the settings that a provider provides are semantically distinct enough to never be wanted at the same time. An example of this could be a network provider, which provides both settings and regularly updated network diagnostics. It may be unacceptable to continually notify upstream providers of settings changes every time the network diagnostics update, and so having two interfaces seems to make sense.

However, in this case we advise that provider authors organise their data into a heirarchy to allow for O(1) equality testing of immutable parts of state which don't change. This allows filtering providers to present just a part of the data upstream.

On the other hand, having multiple upstream interfaces incurs the cost of increasing complexity of the conceptual model of metasetter, the implementation of the core code, and in the implementation of each provider.

#Why can't providers be configured with settings in their init?

To simplify the model, there is only one way of configuring a provider: through its settings provider. The provider interface for settings allows these to be changed at runtime, providing flexibility to the system. Providers which don't choose to handle the settings updates themselves will crash when their settings change, and be restarted by their supervising provider (probably ProviderTree).

Of course, sometimes it's useful to configure static settings for a provider, including at the root of provider trees. In this case, we provide the GenericProvider; this is a factory to create providers at compile time (probably provider load time) given some settings.