If you're more interested in Yetibot's internals or building features, check out the Developer Guide.

Getting help

Although we hope the docs are useful and comprehensive, don't hesitate to ask for help in Slack! It could potentially save you a lot of time.

Up and running

To get a Yetibot running you need:

Some config

A Postgres database

A way to run it (i.e. docker or lein)

Docker Compose

Docker Compose satisfies these requirements very quickly. Run from the root of the Yetibot repo:

docker-compose up

This starts up a Postgres container and a Yetibot container, configured to connect to IRC as user name yetibot_demo. Once it's up check out http://localhost:3456 to view the dashboard.

See the docker-compose.yml file to look at exactly how these containers are configured. This demonstrates a very minimal default config that you can modify. For example, you could use Slack instead by switching to a config like:

If you decide to override the default command, make sure you activate the docker profile (e.g. lein with-profile +docker run) as this properly specifies the .java.policy location, which is necessary for the JVM Sandbox utilized by the clj command.

Kubernetes with Helm

Running on Kubernetes is super simple with the official yetibot-helm chart.

These are equivalent and both immutable (meaning they cannot change at runtime).

Rationale

Yetibot supports both immutable and mutable configuration, for configuring different parts of the system.

It's useful to change the configuration of a system at runtime in certain situations. It would be burdensome to have to login to a system where Yetibot runs, change config and restart Yetibot.

On the other hand, the benefits of immutability are well-known. Explicitly separating out the small amount of mutable config from the majority of immutable config lets us maximize immutability benefits and minimize negative affects of mutability in our system.

Modes

Immutable

Immutable config sources include both profiles.clj and environmental variables via environ and loading EDN from a file at config/config.edn (this can be overridden by specifying a CONFIG_PATH env var).

Any config specified in an EDN file will be overridden by values provided by environ. Environment config can be explicitly ignored by setting an environment variable YETIBOT_ENV_CONFIG_DISABLED=true.

Providing config via multiple methods makes it compatible with 12-factor configuration and simple usage in container environments while still retaining the ease of use of the EDN file option.

The majority of configurable sub-systems use immutable config as they do not need to change very often. Examples include:

Chat adapters

Twitter credentials

Postgres connection string

etc.

Mutable

All mutable config is stored in the database.

A much smaller subset of commands need mutable config, e.g.:

IRC channels

Channel settings

Prefixes

All immutable config, regardless of the source can be prefixed with either yb or yetibot. Examples:

edn

{:yetibot {:log {:level "debug"}}}

{:yb {:url "yetibot.com"}}

Profile

{:prod
{:env
{:yb-twitter-consumer-key "foo"}}}

Environment

YB_GIPHY_KEY="123"
YETIBOT_COMMAND_PREFIX=","

Merged result

If you decided to configure Yetibot through all available means demonstrated above (not recommended but shown for purpose of illustration 😅), the merged config data structure would be:

Poking the runtime

Yetibot has a couple ways to poke around internal state at runtime. One is the typical-yet-amazing practice of exposing an nREPL. The other is its built in eval command. Let's take a look.

nREPL

Yetibot exposes its nREPL on port 65432 by default. You can override this at config path: :nrepl :port.

This allows you to connect your local nREPL-based tools (e.g. vim-fireplace, CIDER, Cursive, or many others), load namespaces and evaluate expressions to read or mutate state making it the ultimate runtime debugging tool.

Eval

Because it's so powerful it must be explicitly enabled per user in configuration. This is an admin-only feature, and maybe shouldn't be enabled at all for anyone.

⚠️ Warning ⚠️

Be careful with eval! It has no limits. You can:

mess up your Yetibot or bring it down

destroy stuff

expose your secrets to others in the channel

Here are some examples at poking around with eval:

;; look at the state of the adapters
!eval (map (juxt yetibot.core.adapters.adapter/uuid yetibot.core.adapters.adapter/connected? yetibot.core.adapters.adapter/connection-latency) (yetibot.core.adapters.adapter/active-adapters))

Dashboard

Yetibot runs a web server that hosts a client-side web dashboard. The dashboard is powered by the yetibot-dashboard NPM module, written in TypeScript. It compiles down to plain HTML, CSS and JavaScript, which Yetibot consumes and serves as static resources. The dashboard fetches all dynamic data from the local GraphQL API.

By default it runs on port 3003, but you may override this by setting the PORT environment variable.

Health checks

Yetibot has a /healthz endpoint that should be use for heath checks. This endpoint checks the status of all configured adapters. If any adapter reports not being connected, /healthz responds 503 Service Unavailable. Otherwise it responds 200 OK.

Slack auto-reconnect

When a Slack connection is closed, we automatically try to reconnect with exponential back-off, up to 500 times. This usually works to restore the connection eventually unless there is a prolonged network outage and all 500 reconnect attempts are spent.

While a connection is open, we try to keep it open by sending ping event every 10 seconds, and expect a pong back within 5 seconds, as recommended in the Slack RTM docs. If we fail to get a pong back within those 5 seconds, the connection is then marked in-active, causing the above /healthz endpoint to report 503.