Skip to content

Configuration Wizard

Daniel Ellison edited this page Jun 22, 2026 · 7 revisions

Configuration Wizard

make config (which runs python -m kai install config) is an interactive question-and-answer that gathers every setting Kai needs and writes them to install.conf in the project root. In protected mode a separate sudo make install step then reads that file and lays the deployment down on disk; in single-user mode the wizard writes the runtime files directly and there is no apply step. This page explains every prompt the wizard asks: what it controls, what to enter, and when each prompt appears or is skipped.

The wizard is re-runnable. If install.conf already exists from a previous run, its values become the new defaults, so re-running only asks about changes. Hit Enter at any prompt to accept the shown default.

How prompts are gated

A handful of prompts only appear under specific conditions. Four gates account for almost all the conditional behavior:

  • Deployment mode. The first prompt selects protected (root-owned install, sudo make install apply step) or single_user (run from the cloned repo, no root). The Installation paths section only appears in protected mode, and the wizard's closing instructions differ per mode.
  • users.yaml exists. When the canonical users.yaml is present (/etc/kai/users.yaml in protected mode, ${XDG_CONFIG_HOME:-~/.config}/kai/users.yaml in single-user mode), the wizard skips only the User setup block and prints a summary instead. Installation-wide defaults (model, timeout, workspaces, PR review limits) prompt on every run regardless, because users.yaml entries without a per-user override inherit them. Per-user features that used to have global wizard prompts (PR review enable, issue triage enable, GitHub notification routing, OS isolation) are managed in users.yaml or via Telegram commands, not here.
  • Default backend. Backend-specific prompts appear only for the selected backend: Codex auth and binary path on codex, the OpenCode binary path on opencode, the Goose binary path on goose, the LLM provider (and API key on goose) on multi-provider backends, autocompact and effort level on claude. All four backends ship a one-shot reasoner, so the memory extraction prompt appears on every backend.
  • Memory enabled and extraction enabled. Memory tunables are nested two layers deep: most appear only when semantic memory is turned on, and the extraction tunables appear only when extraction is also turned on.

Each section below notes the gate that controls it.

Sections, in order

  1. Deployment mode
  2. Installation paths
  3. Telegram
  4. User setup
  5. Telegram transport
  6. Default backend
  7. Agent
  8. Webhook server
  9. Workspaces
  10. PR review agent
  11. Optional features
  12. Semantic memory
  13. External services

Deployment mode

Deployment mode

Which installation shape to configure.

  • Default: protected
  • Valid: protected, single_user
  • Guidance: protected is the production shape: root-owned install at the install location, secrets under /etc/kai/, a service definition, and a sudo make install apply step. single_user runs Kai directly from the cloned repo under your own account: the wizard writes .env to the project root and users.yaml under ${XDG_CONFIG_HOME:-~/.config}/kai/, and you start the daemon with make run. Pick single_user to evaluate Kai or for a personal dev setup; pick protected for an always-on service.

If you pick single_user on a host that still has a readable /etc/kai/env from a previous protected install, the wizard refuses and tells you to remove the protected leftovers first (sudo rm -rf /etc/kai and sudo rm -f /etc/sudoers.d/kai). The runtime would otherwise keep booting from the protected files and silently ignore the single-user ones.


Installation paths

Protected mode only. In single-user mode the install and data directories are the project root and the service user is your own account; none of these prompts appear.

Install location

Where Kai's source code, virtual environment, and launcher script live after sudo make install. Owned by root and read-only to the service user.

  • Default: /opt/kai
  • Valid: any absolute path
  • Guidance: the default is fine on both macOS and Linux. The only reason to change it is if /opt is on a small partition or you have a site convention putting third-party services elsewhere (e.g., /usr/local/kai). Whatever you pick, make install writes there as root.

Data directory

Where runtime data lives: the SQLite database, logs, conversation history, the per-user MEMORY.md files, file uploads, and crash-recovery flags. Owned by the service user and writable to it.

  • Default: /var/lib/kai
  • Valid: any absolute path
  • Guidance: the default follows the FHS convention for service data on Linux and works equally well on macOS. The data directory is intentionally separate from the install directory so reinstalls cannot destroy your conversation history or memory. If you change it, make sure the chosen path is on a volume with enough room for logs and the database to grow.

Service user

The OS user the daemon runs as.

  • Default: kai
  • Valid: any non-empty username
  • Guidance: use a dedicated, low-privilege account. Create it ahead of time (dscl . -create on macOS, adduser on Linux) and let sudo make install set ownership of the install and data directories accordingly. Running as your everyday user works in development but is not the right shape for an always-on service.

Platform

Which service definition to install: a launchd plist on macOS, or a systemd unit on Linux.

  • Default: auto-detected from sys.platform
  • Valid: darwin, linux
  • Guidance: accept the auto-detected value unless you are deliberately preparing a config for a different host. Cross-platform configs do not transplant cleanly because path conventions and service tooling differ.

Telegram

Telegram bot token

The auth token Telegram issues when you create a bot.

  • Default: none (required)
  • Valid: the token string from BotFather, in the form 1234567890:ABC...
  • Guidance: create a bot by messaging @BotFather and running /newbot. Paste the token here. The token is a secret; the wizard writes it to install.conf with mode 0600 and the install step redeploys it to the runtime env file.

User setup

The wizard checks for the canonical users.yaml: /etc/kai/users.yaml in protected mode, ${XDG_CONFIG_HOME:-~/.config}/kai/users.yaml in single-user mode. If it exists, this entire block is skipped and the wizard prints a one-line summary (user and admin counts) plus a reminder that user changes go through Telegram commands or by editing the file directly. A stray users.yaml in the project root is ignored with a note telling you to move or remove it. The prompts below only appear on a fresh install.

Admin Telegram ID

The numeric Telegram user ID of the admin who will be the first user authorized to talk to the bot.

  • Default: none (required)
  • Valid: a positive integer
  • Guidance: message @userinfobot on Telegram to find your numeric ID. The wizard rejects anything that is not a positive integer; usernames like @kai are not accepted. The admin role grants access to every command including the per-user settings management commands.

Admin display name

A short label for the admin entry in users.yaml.

  • Default: admin
  • Valid: letters, digits, spaces, hyphens, underscores
  • Guidance: any short name works. This is shown in /users listings and in some log lines; it is not used for authentication.

Configure advanced user options

Whether to expose per-user advanced fields during this wizard run.

  • Default: No
  • Valid: y/n
  • Guidance: answer No unless you want to set the admin's os_user (process isolation account) here. You can always add advanced fields later by editing users.yaml directly. There is no prompt for home_workspace; every user lands in a per-user directory under the data directory, and an admin who wants a different home adds home_workspace: /absolute/path to their entry by hand.

OS user for subprocess isolation

Only shown when advanced options are enabled. Sets os_user for the admin entry, so the inner agent process for the admin runs as that OS user via sudo -u.

  • Default: the current $USER
  • Valid: letters, digits, dots, hyphens, underscores (empty to skip)
  • Guidance: leave it at the default unless you have created a dedicated account for the admin's agent subprocesses. An empty value, or a value equal to the bot's own user, spawns the agent in-process with no sudo wrap. Process isolation is most useful when several humans share the deployment.

Where the first-time users.yaml is written depends on deployment mode: protected mode stages it under ~/.cache/kai-install/ so sudo make install can copy it into /etc/kai/users.yaml; single-user mode writes the XDG path directly.


Telegram transport

How the bot receives updates from Telegram. This prompt follows User setup; it has no section banner of its own.

  • Default: polling
  • Valid: polling, webhook
  • Guidance: polling is the right choice for almost every deployment. The bot opens a long-poll connection to Telegram and receives updates over it; no inbound port is exposed to the public internet. Choose webhook only if you have a publicly-routable HTTPS endpoint (with a valid certificate) that Telegram can reach, and you specifically want push delivery instead of pull. See Exposing Kai to the Internet for the reverse proxy setup.

When webhook is selected, two more prompts follow:

Telegram webhook URL

The URL Telegram should POST updates to.

  • Default: none (required when transport is webhook)
  • Valid: an HTTPS URL ending in a path you control
  • Guidance: must be HTTPS with a real certificate (Telegram refuses self-signed). Typically this is https://your-domain/webhook/telegram proxied via Cloudflare Tunnel or a public reverse proxy.

Telegram webhook secret

An optional secret that Telegram includes in the X-Telegram-Bot-Api-Secret-Token header on each delivery, used to verify the request is from Telegram.

  • Default: empty (no verification)
  • Valid: any string
  • Guidance: set a long random string. Without it, anyone who learns the URL can POST fake updates. If you are running behind Cloudflare, this complements the IP-allowlist enforcement.

Default backend

Which agent runtime drives the inner conversation.

  • Default: claude
  • Valid: claude, codex, goose, opencode
  • Guidance: claude uses the Claude Code CLI and benefits from Anthropic's subscription auth. codex uses the OpenAI Codex CLI, also subscription-capable. opencode is a multi-provider runtime driven over ACP; goose is Block's multi-provider runtime. See Multiple Backends for the trade-offs. The choice here is the global default; individual users can override backend in their users.yaml entry, and the wizard does not collect setup for per-user overrides (install and authenticate those backends out of band).

The follow-up prompts depend on the chosen backend.

Codex auth mode

Codex backend only.

  • Default: subscription
  • Valid: subscription, api_key
  • Guidance: subscription uses the ChatGPT plan login stored by codex login; no API key is collected. api_key prompts for OPENAI_API_KEY (required) and bills per token.

Codex binary path

Codex backend only. Persisted so the install step writes both the runtime env and the sudoers rule from the same value.

  • Default: which codex, falling back to /opt/homebrew/bin/codex
  • Valid: an existing executable path
  • Guidance: accept the detected path. In subscription mode the wizard reminds you to run codex login after install as each target os_user (run as that user directly, not via sudo from another account).

OpenCode binary path

OpenCode backend only. Persisted so the install step writes both the runtime env and the sudoers rule from the same value.

  • Default: which opencode, otherwise empty (you must type the path)
  • Valid: an existing executable path
  • Guidance: OpenCode has no single canonical install location, so the wizard will not guess. After install, authenticate at least one provider as the service user with opencode auth login; Kai passes the active model at process spawn and OpenCode resolves credentials from its own auth store.

LLM provider

Multi-provider backends only (goose, opencode).

  • Default: none (must be selected)
  • Valid: anthropic, deepseek, google, ollama, openai, openrouter
  • Guidance: pick the provider whose credentials you have. This drives the per-role model registry lookup at runtime. openrouter and ollama are open-ended (any model ID works); the others have curated model lists in the Agent prompt below. ollama needs no API key because it runs locally.

<PROVIDER>_API_KEY

Goose backend only; the variable name shown matches the chosen provider. Skipped for ollama (no key) and for opencode entirely, whose auth is managed by opencode auth login rather than by Kai.

  • Default: none (required for paid providers on goose)
  • Valid: the secret string the provider issued
  • Guidance: generate the key in the provider's console. The wizard stores it in install.conf with mode 0600 and the install step writes it into the runtime env file.

Agent

Installation-wide agent defaults. These prompt on every wizard run, even when users.yaml exists, because users.yaml entries without a per-user override inherit them; skipping the prompts on re-runs used to let a stale global model survive a backend switch.

Default model

The global default model for the conversational role. Curated surfaces present a numbered choice ("Default model"); open-ended surfaces ask for a free-text "Default model ID".

  • Default: the existing value when it is still valid for the chosen backend; otherwise the backend-aware default: opus (claude/anthropic), gpt-5.5 (codex), gpt-5.5-pro (openai), gemini-2.5-pro (google), deepseek-v4-pro (deepseek)
  • Valid (claude): opus, sonnet, haiku
  • Valid (codex): the Codex CLI's own curated list (gpt-5.5, gpt-5.4, ...), which is independent of the OpenAI API surface
  • Valid (goose on a curated provider): the provider's curated list, e.g. gpt-5.5-pro/gpt-5.5/gpt-5.4-family (openai), gemini-2.5-pro/gemini-2.5-flash/gemini-2.5-flash-lite (google), deepseek-v4-pro/deepseek-v4-flash (deepseek)
  • Valid (opencode, and goose on openrouter/ollama): free text; opencode takes a full provider/model ID (e.g. deepseek/deepseek-v4-pro)
  • Guidance: the defaults follow the rule that the conversational role gets the provider's strongest model; non-conversational roles run on cheaper tiers via the registry. If a previously configured model is invalid for the newly selected backend, the wizard says so and prefills the backend-aware default instead. Per-user overrides via /model always win at runtime, so this is the global fallback, not a hard ceiling.

Customize per-role models (PR review, triage, memory, eval)

Optional walk through the six non-conversational roles, in prompt order: pr_review, issue_triage, behavioral_judge, behavioral_gen, memory_extraction, memory_episode.

  • Default: No on a fresh install; Yes when overrides were captured on a previous run
  • Valid: y/n, then one model per role (curated choice or free text, same surface rules as the default model)
  • Guidance: answer No to use the registry defaults, which are tuned per backend (background roles generally run on a cheaper tier than chat). If you answer Yes, accepting the shown default for a role leaves it on the registry value; only values that differ from the registry default are persisted (as DEFAULT_MODELS_JSON), which sits below per-user models: entries and above the registry in the resolution chain.

Agent timeout (seconds)

How long the inner agent subprocess can run on one response before Kai gives up on it.

  • Default: 120
  • Valid: any positive integer
  • Guidance: 120 seconds is a sensible starting point for chat. Long-running tool use (large file edits, broad searches, multi-step refactors) routinely needs more; bumping to 240-300 is reasonable on a fast machine. Going below 60 risks aborting the model mid-thought on legitimate work. If you set it too low you will see synthetic timeout entries in history. Per-user timeout in users.yaml or /settings timeout overrides it at runtime.

Max session age in hours (0 = no limit)

Recycles a user's agent subprocess once the session is older than this, regardless of activity. Applies to every backend.

  • Default: 0 (no limit)
  • Valid: any non-negative number
  • Guidance: long-lived CLI processes can accumulate memory; a 4-8 hour cap is a reasonable guard on memory-constrained machines. 0 disables age-based recycling entirely. Recycling ends the session; the next message starts fresh.

Idle eviction timeout in seconds (0 disables)

Evicts a user's agent subprocess after this much inactivity. Applies to every backend.

  • Default: 1800
  • Valid: any non-negative integer
  • Guidance: 30 minutes keeps the pool small without making users pay a cold start on every message. Raise it if cold starts annoy you and RAM is plentiful; set 0 to keep subprocesses alive indefinitely.

Autocompact threshold (% of context window, 0 = default ~83%)

The fraction of the active context window at which Claude Code automatically compacts conversation history into a summary to free room for new turns. Claude backend only; other backends skip this prompt.

  • Default: 80
  • Valid: 0 to 100
  • Guidance: 80 is a good default. Lower values (60-70) compact sooner, which reduces token usage at the cost of losing raw context earlier in long sessions. Values above ~83 are silently clamped by Claude Code, so setting 100 does not actually disable compaction. Setting 0 falls back to Claude Code's own ~83% default; pin a value here if you want predictable behavior across upstream changes.

Inner Claude effort level

Maps to Claude Code's --effort flag, which trades reasoning depth against latency and token spend. Claude backend only; other backends skip this prompt.

  • Default: high
  • Valid: low, medium, high, xhigh, max
  • Guidance: high is the recommended default for everyday work. xhigh and max produce noticeably better answers on hard problems but are slower and burn more tokens. low and medium are appropriate for quick lookups or tight tool loops. The wizard's allow-list mirrors the runtime allow-list, so it cannot accept a value the daemon would later reject. Claude Code's upstream default has changed over time, so pinning a value here insulates you from future shifts.

Webhook server

These two settings configure Kai's own HTTP server, which serves the /webhook/* and /api/* endpoints. They are unrelated to the Telegram-webhook transport (which is about how Telegram delivers messages to Kai); these are about the server Kai itself runs locally.

Webhook port

The TCP port the embedded HTTP server binds to.

  • Default: 8080
  • Valid: 1 to 65535
  • Guidance: 8080 works for almost every deployment because the public surface is fronted by Cloudflare Tunnel or a reverse proxy. Pick a different port only if 8080 is already in use on the host. Ports below 1024 require root, which Kai cannot bind, so anything in the 1024-65535 range is safe.

Webhook secret

The shared secret required by the X-Webhook-Secret header on every /webhook/* and /api/* request.

  • Default: auto-generated 64-hex-char random string on first run
  • Valid: any non-empty string (required)
  • Guidance: accept the auto-generated value. It is cryptographically random and long enough to resist brute force. Only change it if you have a reason to rotate (for example, a previous value leaked). Note that any external service that calls Kai's API needs this secret in its config too, so a rotation requires updating those callers in lockstep.

Workspaces

Installation-wide defaults; both prompts fire on every wizard run. Users.yaml entries can override workspace_base per user, and users manage additional allowed workspaces at runtime via /workspace allow and /workspace deny.

Workspace base directory

A directory whose immediate subdirectories become valid workspace names. With this set, /workspace foo resolves to <base>/foo without needing the absolute path.

  • Default: empty
  • Valid: any path (absolute or starting with ~)
  • Guidance: point this at your projects parent directory (commonly ~/Projects). Leave it blank if you always use absolute paths in /workspace commands.

Allowed workspaces (comma-separated paths, optional)

Pinned absolute paths that are always valid workspace destinations even if outside WORKSPACE_BASE.

  • Default: empty
  • Valid: comma-separated absolute paths
  • Guidance: use this for repos under non-standard locations. Each path becomes a one-tap option in the /workspaces keyboard. Leave it blank if WORKSPACE_BASE covers everything you need.

PR review agent

There is no enable prompt here: whether reviews run at all is per-user (pr_review in users.yaml, or /github reviews on|off in Telegram), and the wizard prints a reminder saying so. The two prompts below are global resource limits that apply to any review subprocess regardless of which user triggered it. Issue triage and GitHub notification routing are likewise per-user (issue_triage / /github triage, github_notify_chat_id / /github notify) and have no wizard prompts at all.

Review cooldown in seconds (prevents spam from rapid pushes)

Minimum time between automatic review runs on the same PR.

  • Default: 300
  • Valid: any positive integer
  • Guidance: 300 (5 minutes) is calibrated to absorb rapid back-to-back pushes without flooding the PR with reviews. Lower it (60-120) if you want faster feedback during active development; raise it (600+) if reviews are noisy on your setup.

Review subprocess timeout in seconds

Hard cap on how long a single PR review run may take.

  • Default: 900
  • Valid: any positive integer
  • Guidance: 900 (15 minutes) accommodates large PRs without prematurely cutting off real work. If your PRs are typically small, 300-600 keeps a stuck process from running too long. If they are very large (many files, deep refactors), 1200-1800 may be needed. The bot kills the subprocess at the boundary.

Optional features

Voice transcription

Enables inbound voice-message handling. Voice notes get transcribed via whisper.cpp and the text is fed into the conversation as if you typed it.

  • Default: No
  • Valid: y/n
  • Guidance: turn this on if you talk to Kai from your phone and want voice input. Requires ffmpeg on the host and the models/ggml-base.en.bin whisper model (148 MB, gitignored). The wizard does not download it for you; see Voice Setup for installation.

Text-to-speech

Enables outbound voice replies via Piper TTS.

  • Default: No
  • Valid: y/n
  • Guidance: complement to voice transcription. Off by default because most replies are long enough that text is more useful. Worth turning on if you primarily use voice. See Voice Setup for the Piper model files.

After these two prompts the wizard prints a reminder that per-user OS isolation lives in users.yaml (os_user); there is no global subprocess-user prompt.


Semantic memory

Kai's semantic memory layer indexes facts and episodes in Qdrant via Mem0 and surfaces them on every turn as a [Relevant memories...] block. This whole section toggles that subsystem.

Enable semantic memory (Mem0 + Qdrant)

Master switch.

  • Default: No
  • Valid: y/n
  • Guidance: turn it on if you want long-term memory of facts the user shares ("I live in Toronto", "use Celsius"), prior decisions, and episodes (compact narratives of completed tasks). Off, the bot is purely conversational and forgets everything outside its rolling conversation context. See Memory for how retrieval works.

When memory is enabled, the prompts below appear.

Enable memory extraction (proactive memory writes)

Whether the bot runs a background one-shot model call after each turn to identify and store new facts.

  • Default: No
  • Valid: y/n
  • Guidance: turn it on. Without extraction, memory only contains facts saved explicitly; with it, the bot proactively maintains its own memory and quality climbs over time. The extraction reasoner and model resolve per-user from each user's effective backend via the model registry (all four backends ship a one-shot reasoner); there are no global reasoner or model prompts. On codex, opencode, and goose installs the wizard re-prompts for the backend binary path here if it was not already collected. The remaining prompts in this section all hang off this answer.

Per-extraction timeout in seconds

Hard cap on a single stage-1 extraction call.

  • Default: 10
  • Valid: any positive integer
  • Guidance: the dataclass default of 10 is too aggressive for production. Real extractions routinely take 20-30 seconds, especially on the first call after a cold start, and silently abort at the boundary. Set this to 60 or higher unless you know your extractions complete quickly.

Consolidation candidate count (0 disables consolidation)

How many existing similar facts to show the extractor as context. The extractor uses these to decide whether a new observation is genuinely new, an update of an existing fact, or redundant.

  • Default: 8
  • Valid: any non-negative integer
  • Guidance: 8 is the validated default. Lower values shrink the extraction payload at the risk of duplicate facts; higher values give the model more context per call. 0 is a valid kill-switch: the extractor falls back to all-new outputs and the EXISTING FACTS data block is omitted, useful for debugging the consolidation path or forcing a re-extraction sweep.

Episode classifier context turns

How many prior conversation exchanges are shown to the episode classifier as background. The classifier decides whether the just-completed turn warrants generating a stage-2 episode.

  • Default: 3
  • Valid: 0 to 10
  • Guidance: 3 is enough context for the classifier to recognize multi-turn workflows without ballooning the per-call payload. Lower it to 0 to disable the windowing and classify on the current turn alone. Raise it (toward 10) if your tasks span many short exchanges and the classifier is missing them. The 0-10 cap exists so a typo (3000 instead of 3) cannot blow past the model's per-call token limit.

Per-episode timeout in seconds (minimum 10)

Hard cap on a single stage-2 episode call.

  • Default: 120
  • Valid: integer of at least 10
  • Guidance: 120 seconds accommodates the model generating all seven episode fields without rushing. Bump to 180-300 if you see persistent timeouts. The 10-second floor exists because model warm-up alone routinely runs several seconds; a sub-floor timeout would surface as systematic timeouts that mask real model failures as configuration errors. The episode model resolves per-user from the registry; there is no model prompt.

Paraphrase-dedup threshold (cosine; 1.0 effectively disables, 1.01 unambiguously)

Write-time duplicate gate: a new fact whose cosine similarity to an existing fact meets this threshold is treated as a paraphrase and not stored again.

  • Default: 0.9
  • Valid: 0.3 to 1.01
  • Guidance: 0.9 catches near-identical rephrasings while letting genuinely new variants through. Lower it if your store accumulates paraphrase duplicates; raise it toward 1.0 if distinct facts are being swallowed. 1.01 is the unambiguous disable sentinel (no cosine similarity can reach it).

Memory context token budget per turn

Cap on how many tokens of retrieved memory are injected into each conversation turn. Filters the search results before injection.

  • Default: 2000
  • Valid: any positive integer
  • Guidance: 2000 is a balance between giving the model useful background and not displacing the actual conversation. Lower it (1000-1500) if memory injection is crowding out tool results and recent history; raise it (3000-5000) if you have rich memory and the bot keeps missing facts that should be in scope.

Memory search result limit per query

Maximum number of results returned by Qdrant before the token-budget filter applies.

  • Default: 10
  • Valid: any positive integer
  • Guidance: 10 is the validated default. Lower values reduce noise and shave embedding-search cost per turn; higher values pay slightly more compute but catch more relevant facts. Increase to 15-20 only if you find specific facts are getting buried.

External services

Perplexity API key (optional)

Lets the inner agent call Perplexity Sonar via the /api/services/perplexity proxy for web search and synthesis.

  • Default: empty (Perplexity calls disabled)
  • Valid: the key string from the Perplexity dashboard
  • Guidance: add the key if you want web-search grounded answers. The bot prefers Perplexity over generic WebSearch/WebFetch because results are cleaner and citations are returned. Free tier is enough for casual use. See External Services for the request shape and the available service catalog.

After the wizard

The wizard writes install.conf to the project root with mode 0600 and prints a closing message that depends on deployment mode.

Protected mode:

Configuration written to /path/to/kai/install.conf
Review the file, then run: sudo python -m kai install apply

The next step is sudo make install, which reads install.conf, lays out the file system, generates the runtime env file under /etc/kai/, configures sudoers, generates the launchd plist or systemd unit, and starts the service. See Protected Installation for what that step does in detail.

Single-user mode: the wizard additionally writes .env (mode 0600) to the project root and reports where users.yaml lives, then prints:

Single-user mode does not require 'sudo make install'.
Start the daemon with: make run

In both modes you can review install.conf before proceeding, but the right way to change a setting is to re-run the wizard so its validation logic gets to vet the new value.

Clone this wiki locally