Skip to main content
The bot is configured entirely through environment variables. Effect’s Config module reads and validates them at startup — the process exits immediately if a required variable is missing or malformed.

How configuration works

The bot uses Effect’s built-in Config module. Variables marked with Config.redacted are treated as secrets: their values are never printed to logs or traces. Variables read with Config.string or Config.boolean may appear in diagnostic output. Several services use a nested config provider that maps a flat UPPER_SNAKE_CASE environment variable to a dotted key path. For example, AutoThreads wraps its config with:
ConfigProvider.fromEnv().pipe(
  ConfigProvider.nested("autothreads"),
  ConfigProvider.constantCase,
)
This means the internal config key keyword is resolved from the environment variable AUTOTHREADS_KEYWORD. The constantCase transformer converts AUTOTHREADS_KEYWORDautothreads.keyword automatically.

Required

These variables must be set for the bot to start.
DISCORD_BOT_TOKEN
string
required
Discord bot token from the Discord Developer Portal. Used to authenticate the bot with the Discord Gateway and REST API.Read with Config.redacted — the value is never logged.
token: Config.redacted("DISCORD_BOT_TOKEN")
OPENAI_API_KEY
string
required
OpenAI API key. Used by the AI features (thread title generation, documentation generation, summarization) via the @effect/ai-openai integration.The bot calls the following models:
  • gpt-5.2 — primary chat model for AI responses
  • gpt-5.2-codex — code-focused tasks
  • gpt-4.1-mini — lightweight tasks
Read with Config.redacted — the value is never logged.
apiKey: Config.redacted("OPENAI_API_KEY")

Optional

These variables are not required. They either have defaults or enable opt-in features.
DEBUG
boolean
default:"false"
Set to true to enable verbose logging. When false, the minimum log level is Info. When true, all log levels including Debug and Trace are emitted.
Config.withDefault(Config.boolean("DEBUG"), false)
  .pipe(Config.map((debug) => (debug ? "All" : "Info")))
GITHUB_TOKEN
string
GitHub personal access token. Required for the /issueify slash command, which creates GitHub issues from Discord messages.Internally this variable is consumed under the github config namespace:
// Github.ts reads Config.redacted("token") under the "github" namespace,
// which resolves to GITHUB_TOKEN in the environment.
Effect.provideService(
  ConfigProvider.ConfigProvider,
  nestedConfigProvider("github"),
)
Read with Config.redacted — the value is never logged.

AutoThreads

The AutoThreads service automatically creates threads for messages in opted-in channels. These variables are prefixed with AUTOTHREADS_ and mapped via ConfigProvider.nested("autothreads") + ConfigProvider.constantCase.
AUTOTHREADS_KEYWORD
string
default:"[threads]"
The keyword that must appear in a channel’s topic for AutoThreads to activate in that channel. Any text channel whose topic contains this string will have every new user message automatically converted into a thread.
Config.string("keyword").pipe(Config.withDefault("[threads]"))
// Resolved from AUTOTHREADS_KEYWORD

NoEmbed

The NoEmbed service suppresses Discord link previews in opted-in channels. These variables are prefixed with NOEMBED_ and mapped via ConfigProvider.nested("noembed") + ConfigProvider.constantCase.
NOEMBED_KEYWORD
string
default:"[noembed]"
The keyword that must appear in a channel’s topic for NoEmbed to activate. Any channel whose topic contains this string will have link embeds suppressed.
Config.withDefault(Config.string("keyword"), "[noembed]")
// Resolved from NOEMBED_KEYWORD
NOEMBED_URL_WHITELIST
string
default:"effect.website"
Comma-separated list of URL substrings. Only embeds whose URL contains at least one of these substrings are suppressed. All others are left untouched.
Config.withDefault(Config.string("urlWhitelist"), "effect.website")
// Resolved from NOEMBED_URL_WHITELIST
Example: NOEMBED_URL_WHITELIST=effect.website,example.com
NOEMBED_URL_EXCLUDE
string
default:"effect.website/play"
Comma-separated list of URL substrings. Embeds whose URL matches any of these are excluded from suppression, even if they match the whitelist. This lets you allow-list specific paths within an otherwise suppressed domain.
Config.withDefault(Config.string("urlExclude"), "effect.website/play")
// Resolved from NOEMBED_URL_EXCLUDE
Example: NOEMBED_URL_EXCLUDE=effect.website/play,effect.website/repl

OpenTelemetry

The bot emits traces using Effect’s OTLP integration (shared/Otel.ts). When HONEYCOMB_API_KEY is absent, traces are sent to a local OTLP endpoint.
HONEYCOMB_API_KEY
string
API key for Honeycomb. When set, traces are exported to https://api.honeycomb.io/v1/traces with the x-honeycomb-team header.Read with Config.redacted and wrapped in Config.option — if not present, the tracer silently falls back to the local OTLP endpoint instead of failing at startup.
const apiKey = yield* Config.redacted("HONEYCOMB_API_KEY").pipe(Config.option)
HONEYCOMB_DATASET
string
default:"discord-bot"
Honeycomb dataset name. Also sets the OTLP serviceName resource attribute. Defaults to "discord-bot" (the service name passed to TracerLayer).
const dataset = yield* Config.string("HONEYCOMB_DATASET").pipe(
  Config.withDefault(serviceName),
)
When HONEYCOMB_API_KEY is not set, the tracer sends spans to http://localhost:4318. You can receive these locally with the Grafana OTEL stack included in docker-compose.yaml.

Summary table

VariableRequiredDefaultDescription
DISCORD_BOT_TOKENYesDiscord bot token
OPENAI_API_KEYYesOpenAI API key
DEBUGNofalseEnable verbose logging
GITHUB_TOKENNoGitHub token for /issueify
AUTOTHREADS_KEYWORDNo[threads]Channel topic keyword for auto-threading
NOEMBED_KEYWORDNo[noembed]Channel topic keyword for embed suppression
NOEMBED_URL_WHITELISTNoeffect.websiteURLs to suppress embeds for
NOEMBED_URL_EXCLUDENoeffect.website/playURLs to exempt from suppression
HONEYCOMB_API_KEYNoHoneycomb tracing API key
HONEYCOMB_DATASETNodiscord-botHoneycomb dataset / service name

Build docs developers (and LLMs) love