Skip to content

Settings

Robert Sfeir edited this page Oct 6, 2026 · 8 revisions

You change settings on the machine that runs VitalAIze: in the VitalAIze app on a Mac (Settings), or with bin/vitalaize setup in a terminal on a Mac or Linux. Both save to the same file, settings.json, so either can change what the other saved. Most changes are in use within a few seconds. A few restart VitalAIze, and only then: what the machine does, the board's port and password, turning the New Relic page on or off, saving this machine's Claude sessions, taking collectors and their port, and the two AWS profiles.

If you would sooner write a file, everything can also live in settings.exs. Anything you leave out uses its default, so you only write the parts that differ. The file in the repo, settings.example.exs, is a good start: copy it to settings.exs and change what you need. What the app or vitalaize setup saves goes on top of it.

The file is one Elixir map. Text goes in "double quotes", lists in [square brackets], nil means "not set", and every line inside the map ends with a comma.

Settings in the Mac app: the Board section, with the settings that restart the board marked

Where the file is

The board looks for it in this order:

  1. the path in the WALLBOARD_SETTINGS environment variable;
  2. settings.exs in the folder the board was installed to;
  3. settings.exs in the folder you started it from.

The Mac app keeps settings.exs and settings.json next to its database (see How it works). On Linux, settings.json is in the folder you unpacked.

The Settings page on the board

Tap Settings at the top of the board. It shows every setting and its value, with secrets as dots, but it cannot change them: it says "Settings are shown here and changed on this machine: open the VitalAIze app, or run vitalaize setup in a terminal." The "page" marks in the tables below mean a setting is shown there and can be changed in the app or with vitalaize setup; the rest are in settings.exs only.

The page also lists Connected machines, with a Disconnect button for each, when the hub takes collectors, and the repositories you chose to ignore in the mailbox, each with Ask again. See Connecting other machines.

Who can open the Settings page

Without a board password (token), the Settings page opens only in a browser on the machine that runs the board. With one, any device that has signed in with it can open it. On a Linux hub with no screen, set token in settings.exs first.

Changing the board password restarts the board, and every device then needs the new one once, because the key that signs the board's cookie comes from the password.

Every setting

Board

Setting Default What it does Page
port 4747 The port the board listens on: http://<this machine's address>:<port>/. page, restart
token nil The board password. With none, anyone on your network can open the board. With one, the first visit from each device needs /?token=<your token> at the end of the address; after that the browser remembers it. Make one with openssl rand -hex 16. page, restart
rotate_seconds 30 Seconds before the board turns to the next tab. 0 stops it. page
timezone "America/New_York" The time zone for every time on the board. page
brand.name "Kyroco" The name in the header. page
brand.logo nil A path to an .svg or .png logo for the header. With none, the name shows as text.
brand.page2_title "Production health · New Relic" The New Relic tab's title.
updates.check true Once a day, ask GitHub for the latest release and show a note by Settings when it is newer. false stops the check. page
korium.enabled true Show the Korium numbers in Trends. page

What this machine does

Setting Default What it does Page
role "both" "both" runs the board and saves this machine's own sessions. "hub" runs the board and keeps only what other machines send. "collector" runs no board: it watches this machine's sessions and streams them to a hub. page, restart

Collectors

On a hub, these let other machines connect. See Connecting other machines.

Setting Default What it does Page
link.enabled false Take collectors: let other machines pair with this hub and stream their sessions to it. Needs archive.enabled. page, restart
link.port 4748 The port collectors stream to. It is not the board's port. page, restart

On a collector, these say what it watches. A collector has no board, so change the first two in the app or with vitalaize setup; the rest are in settings.exs only.

Setting Default What it does
collector.claude_dirs nil The Claude folders to watch. nil finds ~/.claude and every ~/.claude-something folder that holds sessions.
collector.codex_dirs nil The Codex folders to watch. nil finds ~/.codex.
collector.dir nil Where the collector keeps its certificate, its place in each file, and what it has not sent yet. nil is a collector folder where the database would be.
collector.outbox_mb 64 The most disk, in MB, that unsent events may take. When it is full the collector stops reading until the hub is back.
collector.backfill_days 14 On the first start, report sessions changed in this many days.
collector.poll_seconds 2 How often to look for new lines in session files.

Claude

Setting Default What it does Page
claude.config_dirs ["~/.claude"] Every Claude folder you use. Each one is checked with claude agents --json. With more than one, each session shows which account it belongs to. page
claude.poll_seconds 5 How often to check for sessions. Codex uses it too.
claude.long_running_minutes 45 A working session gets a ringed, darker dot after this many minutes. page
usage.poll_seconds 30 How often to read the transcripts for tokens and cost.
usage.days_back 21 How far back to read transcripts.
usage.prices Anthropic's API list prices, checked 2026-09-28 Dollars per million tokens, per model: input, output, cache_read, and context (the model's context size in tokens). A model matches the longest name here that it starts with. Add or change one like prices: %{"claude-opus-5-5" => %{label: "Opus 5.5", input: 4.0, output: 20.0, cache_read: 0.20, context: 1_000_000}}.

Codex

Setting Default What it does Page
codex.enabled true Show and save this machine's Codex sessions. A Codex folder that does not exist is skipped, so this can stay on without Codex. page
codex.dirs ["~/.codex"] Every Codex folder you use. page
codex.idle_minutes 120 How long an idle Codex session stays on the Agents tab after its last turn. page

Alerts

See Alerts for how to set each one up. All are on the page, and take effect at once.

Setting Default What it does
alerts.phone nil Messages (Mac only): your phone number.
alerts.via "iMessage" "iMessage", or "SMS" for a plain text sent through your iPhone.
alerts.slack_webhook nil A Slack incoming webhook address.
alerts.ntfy_topic nil An ntfy topic. Make it hard to guess.
alerts.ntfy_server nil Your own ntfy server. nil means https://ntfy.sh.
alerts.pushover_user nil Your Pushover user key.
alerts.pushover_token nil The API token of an app you made at Pushover. Both Pushover settings are needed.

Budget

From 0.4.0. A limit on what your agents use in a day or a week. When a total passes its limit, a strip on the board says so and one alert goes out (see Alerts). Every limit needs archive.enabled. All are on the page, and take effect at once.

Setting Default What it does
budget.claude_dollars nil Claude spend limit, in dollars at API list prices, as on Trends. nil means no limit.
budget.claude_dollars_per "day" "day" or "week". A week starts on Monday. Days follow the time zone of the hub's own clock, as Trends does, not timezone.
budget.claude_tokens nil Claude token limit. It counts cache reads and writes too, which are most of it.
budget.claude_tokens_per "day" "day" or "week".
budget.codex_tokens nil Codex token limit, counted the same way.
budget.codex_tokens_per "day" "day" or "week".
budget.by_messages, budget.by_slack, budget.by_ntfy, budget.by_pushover true Which channels get budget alerts. A channel sends only when it is set up under Alerts.

A limit is a whole number. The app and vitalaize setup take it with commas or spaces, such as $1,500 or 50 000 000.

The Budget and New Relic sections of the board's Settings page: a Claude spend limit of 200 per week and the four channel switches

GitHub

Read through the gh command, which must be signed in (gh auth login).

Setting Default What it does Page
github.repos [] The repositories to follow, as "owner/name". The workflow files named in this section (the gate, the deploys and the timeline rows) are the first repository's; another repository names its own (below), or has none. The rest of the section applies to each. Dev and Prod follow the first. Left empty, the board follows github.repo alone. page
github.repo "your-org/your-repo" The one repository to follow when repos is empty. Files from before several repositories use this.
github.branch "main" The main branch. page
github.poll_seconds 30 How often to read runs, the merge queue and pull requests.
github.deploy_poll_seconds 120 How often to read the deploy workflows, and each repository's list of self-hosted runners. That list needs admin rights on the repository; after GitHub refuses it, the board asks again once an hour.
github.gate_workflow "ci.yml" The first repository's workflow file that gates merges. Its latest finished run says whether main is green. page
github.gate_check "ci" The check name on pull requests.
github.dev_deploy "deploy-staging.yml" The first repository's dev deploy workflow file. page
github.prod_deploy "deploy-production.yml" The first repository's prod deploy workflow file. page
github.deploy_workflows the two deploy files Deploy workflows to track even when their last run is days old. A file the repository does not have is skipped.
github.lanes CI, Staging, Production The first rows of the "last 6 hours" timeline, each with a label and its workflows. A row shows when one of its workflows ran in those hours. Any other workflow that ran gets a row under its own name after these, up to six rows in all.

A repository with its own workflows

Every repository's timeline shows the workflows that ran in it, whatever they are called, with nothing to set. What a repository other than the first does not get by itself is a gate: with none, its Main comes from the runs its pushes to main started (red when the newest run of any of those workflows failed, green when all passed). To give it a gate or deploy workflows of its own:

  • In the app or vitalaize setup, write them after its name on its line: acme/mobile gate=build.yml dev=deploy-dev.yml prod=deploy.yml. Any of the three can be left out. The first repository's line is its name alone, since its workflows are the three fields below the list.
  • In settings.exs, make its entry a map:
github: %{
  gate_workflow: "ci.yml",
  repos: [
    "acme/api",
    %{repo: "acme/mobile", gate_workflow: "build.yml"}
  ]
}

A map in the file can also change anything else in this section for that repository, such as branch or lanes. A gate file the repository does not have counts as no gate.

Each busy repository costs about 600 GitHub calls an hour, and GitHub allows 5,000, so keep it to about six. See How it works.

Dev awake or asleep (AWS)

Off until you name a read-only AWS profile. Then the Dev tile shows Awake, Asleep, Waking or Going to sleep instead of the last deploy.

Setting Default What it does Page
dev_power.aws_profile nil The AWS profile to read with, from ~/.aws/config. nil turns this off. page, restart
dev_power.region nil nil uses the profile's own region.
dev_power.database nil Dev's database.
dev_power.cluster nil Dev's ECS cluster.
dev_power.poll_seconds 60 How often to check.

Prod builds (AWS)

Off until you name read-only AWS profiles for prod and dev (builds.dev_profile, or dev_power.aws_profile). Then the Prod tile says Current when prod runs the same images as dev, and Behind when dev has a different one.

Setting Default What it does Page
builds.prod_profile nil The AWS profile for prod. page, restart
builds.dev_profile nil The AWS profile for dev. nil uses dev_power.aws_profile.
builds.region "us-east-1" The region.
builds.repository nil The ECR repository both environments' images come from.
builds.dev %{cluster: nil, services: []} Dev's ECS cluster and services.
builds.prod %{cluster: nil, services: []} Prod's ECS cluster and services.
builds.poll_seconds 60 How often to check.

Archive

Setting Default What it does Page
archive.enabled true Keep every session in the database, for Archive and Trends. Off also turns off everything that needs the database: collectors, the saved GitHub runs and pull requests (so Shipped and CI minutes on Trends), budget limits, and announcing the board on your network.
archive.collect_local true Save this machine's own sessions. Off makes a hub that only keeps what other machines send. role: "hub" turns it off whatever is set here. page, restart
archive.advertise true Announce the board on your network, so collectors find it.
archive.path nil Where the database lives. nil picks the usual place (see How it works).
archive.machine nil This machine's name in the database. nil uses its network name.
archive.backfill_days 14 Days of sessions, GitHub runs and pull requests to save on the first start. It is also the furthest back the board reaches for runs it missed while it was off. page
archive.settle_seconds 120 Save a session once it has been quiet this long. page
archive.poll_seconds 60 How often to look for sessions to save.
archive.github_poll_seconds 300 How often to save GitHub runs, jobs and merged pull requests. Once a day it also reads whether each repository is public and its default branch.
archive.github_jobs_per_round 100 At most this many runs get their jobs saved each round (one GitHub call each, or one per 100 jobs for a bigger run). Every attempt's jobs are saved.

New Relic

The New Relic tab, read through its NerdGraph API. The checks are set in settings.exs only, so the tab is empty until you write new_relic.checks there.

Setting Default What it does Page
new_relic.enabled true false removes the tab: the board never asks the keychain, 1Password or New Relic for anything. page, restart
New Relic API key none From 0.4.0. Type your New Relic User API key (it starts with NRAK-) in the app or vitalaize setup. It is kept in your login keychain on a Mac, and in keys/new_relic beside the database on Linux, a file only your user can read; never in settings.json. It is shown only as dots or "set". Type a new one to replace it; empty the field (- in vitalaize setup) to remove it. A typed key wins over 1Password. page
new_relic.api_key_ref nil Or where the key lives in 1Password, like "op://Private/New Relic/credential", read with 1Password's op command. Used only when no key is typed. The key is read again whenever either changes, with no restart, and kept in memory only. page
new_relic.account_id nil Your New Relic account number. page
new_relic.region "us" "us" or "eu". page
new_relic.poll_seconds 60 How often to check.
new_relic.checks [] The checks to show. The first is the big one; the rest fill the "More checks" row. Each is %{name: "Heartbeat", monitor: "Name of the synthetic monitor"} or %{name: "Errors today", nrql: "SELECT count(*) FROM TransactionError SINCE today", unit: ""}.
new_relic.slots 3 How many spaces the "More checks" row has, filled or empty.

Your look

The theme section holds every color and font, as CSS values: page, surface, text, text_body, text_muted, border, border_strong, track, accent, alert, info, info_light, ok, warn, warn_deep, radius, font_body, font_display, font_css_url, fonts_dir (your own font files; nil uses the ones that come with the board) and font_faces. theme.dark holds the same color names for dark mode. The defaults are Kyroco's colors; see settings.example.exs for each light value. These are in the file only.

In the file only

These are not in the app or vitalaize setup, so change them in settings.exs: brand.logo, brand.page2_title, every poll_seconds, usage.days_back and usage.prices, github.repo, github.gate_check, github.deploy_workflows and github.lanes, everything under dev_power and builds except the two profiles, archive.enabled, archive.advertise, archive.path, archive.machine, the two archive.github_ settings, new_relic.checks and new_relic.slots, collector.dir, collector.outbox_mb and collector.backfill_days, and theme.

Outside the file

  • RELEASE_DISTRIBUTION and RELEASE_COOKIE (environment variables): remote control of a running board is off unless you set RELEASE_DISTRIBUTION. If you do, also set RELEASE_COOKIE to a secret of your own.
  • WALLBOARD_ROLE: when set, it wins over role in the file.

Clone this wiki locally