-
-
Notifications
You must be signed in to change notification settings - Fork 89
Description
Description
Add support for configuration files to the Fedify CLI tools to allow users to set default options and preferences. This will improve the developer experience by eliminating the need to repeatedly specify common options and allowing for project-specific configurations.
Implementation details
Configuration file format and location
Use TOML format for configuration files. TOML was chosen over JSON because it supports comments, has clearer type representation, and is more human-friendly for configuration files. The parser overhead is minimal with the smol-toml library.
Configuration files are searched and merged in the following order (later files override earlier ones):
- /etc/fedify/config.toml — System-wide defaults
- $XDG_CONFIG_HOME/fedify/config.toml (or ~/.config/fedify/config.toml) — User-specific settings
- ./.fedify.toml — Project-specific settings (current working directory)
--config PATH— Additional config file specified via CLI (merged on top)
The single filename .fedify.toml in CWD was chosen for clarity and consistency with modern CLI tools. The leading dot keeps it hidden from ls output.
Configuration merging strategy
Configuration files are deep-merged at the field level:
- Objects: Recursively merged
- Arrays: Completely replaced (no merge)
- Primitives: Replaced
Example:
# /etc/fedify/config.toml
user_agent = "Fedify/1.0"
[lookup]
timeout = 30.0
authorized_fetch = false# ./.fedify.toml
user_agent = "MyBot/1.0" # overrides system config
[lookup]
timeout = 60.0 # overrides system config
# authorized_fetch stays false from system configArrays are completely replaced rather than merged because merging behavior would be ambiguous (append? deduplicate? replace?) and complete replacement is more predictable for configuration.
CLI options
Add two global options to all commands:
--config PATH— Load an additional configuration file and merge it on top of the standard config chain--ignore-config— Ignore all configuration files and use only CLI options and defaults
These two options are mutually exclusive and should produce an error if both are specified.
The --ignore-config flag is useful for debugging configuration issues and for ensuring reproducible behavior in CI environments.
Integration with Optique
Use Optique 0.10.0's @optique/config package with Valibot for schema validation. (Note: As of January 26, 2026, Optique 0.10.0 has not yet been released, but the config integration feature is documented and will be available in that version.) Since Optique's config integration only supports single-file loading, implement custom multi-file merging logic before passing the merged configuration to Optique's bindConfig().
Use Valibot instead of Zod for schema validation because it has better tree-shaking characteristics and smaller bundle size, which matters for CLI tools.
Error handling
- If a configuration file exists but fails to parse, print an error message and skip that file (continue loading other configs)
- If /etc/fedify/config.toml is not readable (permission denied), silently skip it
- If a file specified via
--configdoesn't exist or fails to parse, exit with an error - If both
--configand--ignore-configare specified, exit with an error explaining they are mutually exclusive
Configuration file structure
# Fedify CLI configuration file
# Place at: ./.fedify.toml (project), ~/.config/fedify/config.toml (user),
# or /etc/fedify/config.toml (system)
# Global settings
debug = false
user_agent = "MyBot/1.0 (+https://example.com/bot)"
log_file = "/var/log/fedify.log"
tunnel_service = "localhost.run" # "localhost.run" | "serveo.net" | "pinggy.io"
# WebFinger command
[webfinger]
max_redirection = 5
allow_private_addresses = false
# Lookup command
[lookup]
timeout = 30.0 # seconds
default_format = "default" # "default" | "raw" | "compact" | "expand"
separator = "----"
authorized_fetch = false
first_knock = "draft-cavage-http-signatures-12"
traverse = false
suppress_errors = false
# Inbox command
[inbox]
actor_name = "Fedify Ephemeral Inbox"
actor_summary = "An ephemeral ActivityPub inbox for testing purposes."
authorized_fetch = false
no_tunnel = false
follow = ["@user@example.com"]
accept_follow = ["*"]
# Relay command
[relay]
protocol = "mastodon" # "mastodon" | "litepub"
port = 8000
name = "Fedify Relay"
persistent = "/path/to/relay.db" # optional
no_tunnel = false
accept_follow = []
reject_follow = []
# NodeInfo command
[nodeinfo]
raw = false
best_effort = false
show_favicon = true
show_metadata = falseWhy this is a good first issue
- Clear requirements: Well-defined feature with obvious use cases
- Modern tooling: Leverages Optique's built-in config support (available in 0.10.0)
- Incremental implementation: Can start with basic options and expand gradually
- High impact: Significantly improves developer experience for regular CLI users
- Safe changes: Additive feature that doesn't break existing functionality
- Good documentation: Optique config integration is documented at https://unstable.optique.dev/integrations/config
Acceptance criteria
- Support TOML configuration files using
smol-tomlparser - Implement hierarchical config loading: system → user → project → custom
- Implement deep merge for objects, complete replacement for arrays and primitives
- Add
--config PATHoption to specify additional config file - Add
--ignore-configflag to skip all config files - Validate that
--configand--ignore-configare mutually exclusive - Use Valibot for type-safe schema validation
- Integrate with Optique 0.10.0's
bindConfig()for seamless CLI option merging - Handle config file errors gracefully with helpful error messages
- Silently skip /etc/fedify/config.toml if not readable
- Update documentation with config file format and available options
Files to create/modify
- packages/cli/src/config.ts — New file: config schema (Valibot), merging logic, file loading
- packages/cli/src/globals.ts — Add
configOptionswith--configand--ignore-config - packages/cli/src/mod.ts — Call
loadMergedConfig()and pass to commands - packages/cli/src/webfinger/command.ts — Wrap options with
bindConfig() - packages/cli/src/lookup.ts — Wrap options with
bindConfig() - packages/cli/src/inbox.tsx — Wrap options with
bindConfig() - packages/cli/src/relay.ts — Wrap options with
bindConfig() - packages/cli/src/nodeinfo.ts — Wrap options with
bindConfig() - packages/cli/src/tunnel.ts — Wrap tunnel service option with
bindConfig() - packages/cli/deno.json — Add
valibotandsmol-tomlimports - packages/cli/package.json — Add
valibotandsmol-tomldependencies
Dependencies to add
Add to both deno.json and package.json (project supports both Deno and Node.js/Bun):
{
"dependencies": {
"valibot": "^0.42.0",
"smol-toml": "^1.6.0"
}
}Note: @optique/config should already be available in Optique 0.10.0, but verify the version is at least 0.10.0.
Example usage
# Use default config chain (system → user → project)
fedify lookup @user@example.com
# Add an extra config file on top
fedify lookup @user@example.com --config ./test-config.toml
# Ignore all config files
fedify lookup @user@example.com --ignore-config
# Override specific options (even with config files)
fedify lookup @user@example.com --user-agent "TestBot/1.0"
# Create project-specific config
cat > .fedify.toml << 'EOF'
debug = true
[lookup]
authorized_fetch = true
default_format = "compact"
EOF
# Create user-wide config
mkdir -p ~/.config/fedify
cat > ~/.config/fedify/config.toml << 'EOF'
user_agent = "MyPersonalAgent/1.0"
EOF