Caution
You are not reading the latest stable version of this documentation. If you want up-to-date information, please have a look at 0.3.5.x.
When it comes to running your own server, managing dependencies is perhaps the most difficult part. The term “dependency hell” emerged from this sentiment. Even the best developers have lost significant amounts of time trying to find, correct, or clarify documentation for dependencies or dependency configuration. Individuals who manage their own servers have specific technical skills and are willing to devote the time and effort necessary to maintain them. Companies have entire departments dedicated to this task.
Some other personal server products aimed at non-technical individuals tackle this problem by simply hard coding dependencies. Since StartOS is a platform for running all open-source, self-hosted software, it is not possible to hard code every possible service, service dependency, and service dependency configuration forever. Instead, Start9 built a new, unprecedented operating system that touts a generalized, intuitive approach to dependency management and service configuration. StartOS enables users to easily and selectively install, uninstall, and update any service they want without getting stuck in dependency hell.
This may sound cool or neat, but it is more than that: it’s novel. This has never been done before.
The key to making the system work is a new, domain-specific-language (DSL) and set of standards that are used by developers to define the rules and requirements of their services. Run in the context of StartOS, these rules and requirements appear as simple UI elements, such as inputs, toggles, and drop downs, and they are enforced by validations and clear user instructions. Using this system, what previously required serious time and expertise, can now be done by anyone in a matter of seconds.
This DSL is utilized in the config rules and dependencies key in the service manifest.
Many services depend on other libraries and services on StartOS (such as Bitcoin), sometimes even a particular version of those services. These need to be specified by the developers so that StartOS can handle installing these dependencies under the hood.
Dependencies are defined in the Manifest.
The key of each field in the dependencies object is the lowercase, kebab-case package id of the service that is depended on.
Only the type of requirement must be defined. Requirement types include:
“Required” - which means it’s always required.
“Opt-in” - which means the dependency may be required if you change the service’s config ie. you can opt into the selection
“Opt-out” - which means the dependency will be required according to the default service config ie. you can opt out of the selection
If a dependency requires a more advanced configuration, rule checks and auto configurations can be implemented. These functions utilize a preloaded Docker image within StartOS entitled _compat_. This system utility provides the mechanisms for checking dependency configuration compatibility by providing a file with the defined rules as input.
In this case, each dependency contains a set of rules that need to be fulfilled as true if the dependency is to be properly installed. The “config rules” here are for auto-configuring dependencies - the action defined by the rule will be executed if the service is auto configured with defaults during initial setup. This simplifies and streamlines the user experience. The interface should provide suggestions for the behavior if the denoted rule cannot be met with previous configurations.
Here is an example from BTCPay Server.
First, an except from the dependencies section of the Manifest. We see here that bitcoind
is a dependency of BTCPay Server. It is required to be installed at at least version 0.21.1.2.
There also exists a rule that states the advanced.peers.listen
config option in bitcoind must be equal to true
. If this value is not set as such, SET
the value to true.
These actions all take place during the initial service configuration, before the service is started for the first time.
...
dependencies:
# Key must be the package id of another service that exists in the marketplace
bitcoind:
# The version range that is acceptable for this dependency
version: "^0.21.1.2"
# Describes if the dependency is critical to the service functioning. If the dependency is critical, the service will stop if this dependency is stopped.
critical: false
requirement:
# "Opt-out" means the dependency will be required according to the default config. "Opt-in" means the dependency may be required if you change the config. And "required" just means it's always required.
type: "opt-in"
# An explanation of how to opt-in or opt-out. This value is optional for type=required
how: Can alternatively configure an external bitcoind node.
# Description of the necessity of the dependency relationship
description: Used to fetch validated blocks.
# This defines the actions to check dependency rules and auto configure them if possible
config:
# This levies requirements on the configuration of the dependency and suggests ways to remedy any incompatibilities.
check:
type: docker
image: compat
system: true
entrypoint: compat
args:
- dependency
- check
- btcpayserver
- "bitcoind"
- /datadir
- "/mnt/assets/bitcoind_config_rules.yaml"
mounts:
main: /datadir
compat: /mnt/assets
io-format: yaml
# This implements default values on the configuration of the dependency
auto-configure:
type: docker
image: compat
system: true
entrypoint: compat
args:
- dependency
- "auto-configure"
- btcpayserver
- "bitcoind"
- /datadir
- "/mnt/assets/bitcoind_config_rules.yaml"
mounts:
main: /datadir
compat: /mnt/assets
io-format: yaml
...
Secondly, an except from it’s dependency’s configuration rules file:
- rule: "advanced.peers.listen?"
description: Peer port must be listening on the network.
suggestions:
- SET:
var: advanced.peers.listen
to-value: true
Note
Dependency config rules are processed in order.
Configuring a service to have multiple optional dependencies is also possible. This can be done by defining a file that specifies the condition under which the dependency should become activated.
For example, in BTCPay Server’s config, a user can select either LND or Core Lightning (CLN) as an internal lightning node implementation. To do this, we define the following file:
lnd:
condition: '''lightning.type = "lnd"'
health_checks: []
c-lightning:
condition: '''lightning.type = "core lightning"'
health_checks: []
This file gets passed in as an argument during BTCPay Server’s config set function in its Manifest, shown here.