diff --git a/docs/administration/server-configuration.mdx b/docs/administration/server-configuration.mdx
index 30fbe39e9..0ce3714c6 100644
--- a/docs/administration/server-configuration.mdx
+++ b/docs/administration/server-configuration.mdx
@@ -10,7 +10,7 @@ description: "Server-owned settings.toml sections, CLI overrides, and environmen
On a same-machine setup, the CLI and server share one `settings.toml`. On a remote deployment, the server machine has its own `settings.toml`, and the client machine keeps a separate local `settings.toml` for CLI-only values such as `[cli.target]`.
-Legacy `server.toml`, `user.toml`, and `cli.toml` are ignored with a warning. Rename them to `settings.toml`.
+Fabro only reads `settings.toml`. Older `server.toml`, `user.toml`, and `cli.toml` filenames are no longer part of the supported config surface.
### Which sections are server-owned
diff --git a/docs/administration/troubleshooting.mdx b/docs/administration/troubleshooting.mdx
index 89142fc64..8e818eebb 100644
--- a/docs/administration/troubleshooting.mdx
+++ b/docs/administration/troubleshooting.mdx
@@ -14,7 +14,7 @@ fabro doctor --server https://fabro.example.com/api/v1
```
It checks:
-- Local user config and legacy `~/.fabro/.env` warnings
+- Local user config and storage directory health
- Server-reported LLM provider connectivity
- GitHub App, sandbox, and Brave Search credentials
- Server authentication and crypto configuration
diff --git a/docs/reference/user-configuration.mdx b/docs/reference/user-configuration.mdx
index c9c29f9bf..3c426ba56 100644
--- a/docs/reference/user-configuration.mdx
+++ b/docs/reference/user-configuration.mdx
@@ -8,7 +8,7 @@ Fabro loads machine defaults from `~/.fabro/settings.toml`. The file is optional
On a same-machine setup, the CLI and server both read this file. On a remote setup, each machine has its own `settings.toml` and reads the sections relevant to that process.
-Legacy `cli.toml`, `user.toml`, and `server.toml` are ignored with a warning. Rename them to `settings.toml`.
+Fabro only reads `settings.toml`. Older `cli.toml`, `user.toml`, and `server.toml` filenames are no longer part of the supported config surface.
## File location
diff --git a/lib/crates/fabro-cli/src/commands/config/mod.rs b/lib/crates/fabro-cli/src/commands/config/mod.rs
index 90b0a8586..83b0a29a2 100644
--- a/lib/crates/fabro-cli/src/commands/config/mod.rs
+++ b/lib/crates/fabro-cli/src/commands/config/mod.rs
@@ -21,10 +21,8 @@ fn config_layers(
Some(path) => workflow_and_project_layers(path, cwd)?,
None => (SettingsLayer::default(), load_settings_project(cwd)?),
};
- let user_layer = user_config::settings_layer_with_config_and_storage_dir(
- Some(ctx.base_config_path()),
- None,
- )?;
+ let user_layer =
+ user_config::load_settings_with_config_and_storage_dir(Some(ctx.base_config_path()), None)?;
Ok(EffectiveSettingsLayers::new(
SettingsLayer::default(),
workflow_layer,
diff --git a/lib/crates/fabro-cli/src/commands/doctor.rs b/lib/crates/fabro-cli/src/commands/doctor.rs
index 894dc4da3..58eeb83ae 100644
--- a/lib/crates/fabro-cli/src/commands/doctor.rs
+++ b/lib/crates/fabro-cli/src/commands/doctor.rs
@@ -2,11 +2,7 @@ use std::path::{Path, PathBuf};
use anyhow::Result;
use fabro_api::types as api_types;
-use fabro_config::legacy_env;
-use fabro_config::user::{
- active_settings_path, legacy_old_user_config_path, legacy_server_config_path,
- legacy_user_config_path,
-};
+use fabro_config::user::active_settings_path;
use fabro_types::settings::CliSettings;
use fabro_types::settings::cli::{CliLayer, OutputFormat};
pub(crate) use fabro_util::check_report::{
@@ -22,12 +18,9 @@ use crate::command_context::CommandContext;
use crate::shared::print_json_pretty;
use crate::user_config;
-pub(crate) fn check_config(
- settings_path: Option,
- legacy_paths: &[PathBuf],
-) -> CheckResult {
- match (settings_path, legacy_paths.is_empty()) {
- (Some(path), true) => {
+pub(crate) fn check_config(settings_path: Option) -> CheckResult {
+ match settings_path {
+ Some(path) => {
let display = contract_tilde(&path);
CheckResult {
name: "Configuration".to_string(),
@@ -40,44 +33,7 @@ pub(crate) fn check_config(
remediation: None,
}
}
- (Some(path), false) => {
- let display = contract_tilde(&path);
- CheckResult {
- name: "Configuration".to_string(),
- status: CheckStatus::Warning,
- summary: display.display().to_string(),
- details: std::iter::once(CheckDetail::new(format!(
- "Loaded from {}",
- display.display()
- )))
- .chain(legacy_paths.iter().map(|legacy| {
- let legacy_display = contract_tilde(legacy);
- CheckDetail::new(format!(
- "Ignoring legacy config file {}",
- legacy_display.display()
- ))
- }))
- .collect(),
- remediation: Some("Delete or rename legacy config files".to_string()),
- }
- }
- (None, false) => CheckResult {
- name: "Configuration".to_string(),
- status: CheckStatus::Warning,
- summary: "legacy config files ignored".to_string(),
- details: legacy_paths
- .iter()
- .map(|legacy| {
- CheckDetail::new(format!("Found legacy config file {}", legacy.display()))
- })
- .chain(std::iter::once(CheckDetail::new(
- "Rename one to ~/.fabro/settings.toml or create a new settings.toml"
- .to_string(),
- )))
- .collect(),
- remediation: Some("Create ~/.fabro/settings.toml".to_string()),
- },
- (None, true) => CheckResult {
+ None => CheckResult {
name: "Configuration".to_string(),
status: CheckStatus::Warning,
summary: "no settings config file found".to_string(),
@@ -89,22 +45,6 @@ pub(crate) fn check_config(
}
}
-fn check_legacy_env(path: Option) -> Option {
- path.map(|path| {
- let display = contract_tilde(&path);
- CheckResult {
- name: "Legacy .env".to_string(),
- status: CheckStatus::Warning,
- summary: "legacy secrets file detected".to_string(),
- details: vec![CheckDetail::new(format!(
- "{} is no longer read by fabro",
- display.display()
- ))],
- remediation: Some("Re-enter credentials with `fabro provider login`.".to_string()),
- }
- })
-}
-
#[derive(Debug, Clone, PartialEq, Eq)]
struct StorageDirStatus {
path: PathBuf,
@@ -284,37 +224,20 @@ pub(crate) async fn run_doctor(
};
let settings_config_path = active_settings_path(None);
- let legacy_config_paths = [
- legacy_user_config_path(),
- legacy_old_user_config_path(),
- legacy_server_config_path(),
- ]
- .into_iter()
- .flatten()
- .filter(|path| path.exists())
- .collect::>();
- let legacy_env_path = {
- let p = legacy_env::legacy_env_file_path();
- p.exists().then_some(p)
- };
let settings = user_config::load_settings().unwrap_or_default();
- let storage_dir_path = user_config::storage_dir(&settings)
- .unwrap_or_else(|_| fabro_util::Home::from_env().storage_dir());
+ let storage_dir_path =
+ user_config::storage_dir(&settings).unwrap_or_else(|_| user_config::default_storage_dir());
let storage_dir = probe_storage_dir(&storage_dir_path);
- let mut local_checks = vec![
+ let local_checks = vec![
check_config(
settings_config_path
.exists()
.then_some(settings_config_path),
- &legacy_config_paths,
),
check_storage_dir(&storage_dir),
];
- if let Some(legacy_env_check) = check_legacy_env(legacy_env_path) {
- local_checks.push(legacy_env_check);
- }
let mut report = CheckReport {
title: "Fabro Doctor".to_string(),
@@ -470,45 +393,18 @@ mod tests {
#[test]
fn check_config_pass_with_path() {
- let result = check_config(Some(PathBuf::from("/home/user/.fabro/settings.toml")), &[]);
+ let result = check_config(Some(PathBuf::from("/home/user/.fabro/settings.toml")));
assert_eq!(result.status, CheckStatus::Pass);
assert!(result.summary.contains(".fabro/settings.toml"));
}
#[test]
fn check_config_warning_without_path() {
- let result = check_config(None, &[]);
+ let result = check_config(None);
assert_eq!(result.status, CheckStatus::Warning);
assert!(result.remediation.is_some());
}
- #[test]
- fn check_config_warning_for_legacy_only_path() {
- let result = check_config(None, &[PathBuf::from("/home/user/.fabro/cli.toml")]);
- assert_eq!(result.status, CheckStatus::Warning);
- assert!(result.summary.contains("legacy"));
- }
-
- #[test]
- fn check_legacy_env_warning_when_present() {
- let result = check_legacy_env(Some(PathBuf::from("/home/user/.fabro/.env")));
- assert_eq!(
- result.as_ref().map(|check| check.status),
- Some(CheckStatus::Warning)
- );
- assert!(
- result
- .as_ref()
- .is_some_and(|check| check.summary.contains("legacy secrets file"))
- );
- }
-
- #[test]
- fn check_legacy_env_is_omitted_when_absent() {
- let result = check_legacy_env(None);
- assert!(result.is_none());
- }
-
// -- check_storage_dir --
#[test]
diff --git a/lib/crates/fabro-cli/src/commands/install.rs b/lib/crates/fabro-cli/src/commands/install.rs
index 5a6b44d19..babf6b928 100644
--- a/lib/crates/fabro-cli/src/commands/install.rs
+++ b/lib/crates/fabro-cli/src/commands/install.rs
@@ -16,8 +16,8 @@ use dialoguer::theme::ColorfulTheme;
use dialoguer::{MultiSelect, Select};
use fabro_api::types::{CreateSecretRequest, SecretType as ApiSecretType};
use fabro_auth::{AuthCredential, AuthMethod, codex_oauth_config, credential_id_for};
-use fabro_config::user::{SETTINGS_CONFIG_FILENAME, legacy_default_storage_root};
-use fabro_config::{ResolveError, Storage, envfile, legacy_env};
+use fabro_config::user::{SETTINGS_CONFIG_FILENAME, default_storage_dir};
+use fabro_config::{ResolveError, Storage, envfile};
use fabro_install::{
InstallListenConfig, generate_jwt_keypair, merge_server_settings as merge_server_settings_impl,
write_github_app_settings, write_token_settings,
@@ -1166,7 +1166,9 @@ async fn persist_install_outputs(
settings_write,
server_was_running,
|path| Box::pin(server_client::connect_api_client(path)),
- |path, timeout| Box::pin(stop::stop_server(path, timeout)),
+ |path, timeout| {
+ Box::pin(async move { stop::stop_server(path, timeout).await.unwrap_or(false) })
+ },
)
.await
}
@@ -1355,7 +1357,9 @@ async fn restart_server_after_install(
restart_server_after_install_with(
storage_dir,
config_path,
- |path, timeout| Box::pin(stop::stop_server(path, timeout)),
+ |path, timeout| {
+ Box::pin(async move { stop::stop_server(path, timeout).await.unwrap_or(false) })
+ },
|storage_dir, config_path| {
Box::pin(start::ensure_server_running_for_storage(
storage_dir,
@@ -1478,9 +1482,9 @@ async fn run_install_github_inner(
let storage_dir = user_config::storage_dir(&parsed_settings).unwrap_or_else(|_| {
args.storage_dir
.clone_path()
- .unwrap_or_else(|| legacy_default_storage_root().join("storage"))
+ .unwrap_or_else(default_storage_dir)
});
- let server_was_running = record::active_server_record(&storage_dir).is_some();
+ let server_was_running = record::active_server_record(&storage_dir)?.is_some();
let mut doc: toml::Value = toml::from_str(&existing_config_contents)
.context("failed to parse existing settings.toml")?;
@@ -1623,7 +1627,7 @@ async fn run_install_inner(
let emoji = console::Emoji("⚒️ ", "");
let cli_settings = user_config::load_settings_with_storage_dir(args.storage_dir.as_deref())?;
let storage_dir = user_config::storage_dir(&cli_settings)?;
- let server_was_running = record::active_server_record(&storage_dir).is_some();
+ let server_was_running = record::active_server_record(&storage_dir)?.is_some();
let fabro_dir = fabro_util::Home::from_env().root().to_path_buf();
let config_path = fabro_dir.join(SETTINGS_CONFIG_FILENAME);
let existing_config_contents = std::fs::read_to_string(&config_path).ok();
@@ -1656,18 +1660,6 @@ async fn run_install_inner(
std::fs::create_dir_all(&fabro_dir)
.with_context(|| format!("creating fabro home directory {}", fabro_dir.display()))?;
- {
- let env_path = legacy_env::legacy_env_file_path();
- if env_path.exists() {
- fabro_util::printerr!(
- printer,
- " Warning: {} is no longer read by fabro server. This install will persist runtime secrets in server.env and workflow-visible credentials in the vault instead.",
- env_path.display()
- );
- fabro_util::printerr!(printer, "");
- }
- }
-
let facts = InstallFacts {
codex_detected: detect_binary_on_path("codex").await,
};
diff --git a/lib/crates/fabro-cli/src/commands/provider/login.rs b/lib/crates/fabro-cli/src/commands/provider/login.rs
index 9654145a4..9445e6bac 100644
--- a/lib/crates/fabro-cli/src/commands/provider/login.rs
+++ b/lib/crates/fabro-cli/src/commands/provider/login.rs
@@ -1,7 +1,6 @@
use anyhow::Result;
use fabro_api::types;
use fabro_auth::credential_id_for;
-use fabro_config::legacy_env;
use fabro_types::settings::CliSettings;
use fabro_types::settings::cli::CliLayer;
use fabro_util::printer::Printer;
@@ -36,17 +35,6 @@ pub(super) async fn login_command(
let credential_id = credential_id_for(&credential).map_err(anyhow::Error::msg)?;
let value = serde_json::to_string(&credential)?;
- {
- let path = legacy_env::legacy_env_file_path();
- if path.exists() {
- fabro_util::printerr!(
- printer,
- " Warning: {} is no longer read by fabro server. Re-enter credentials with `fabro provider login`.",
- path.display()
- );
- }
- }
-
server
.api()
.create_secret()
diff --git a/lib/crates/fabro-cli/src/commands/run/command.rs b/lib/crates/fabro-cli/src/commands/run/command.rs
index 81add8adf..9d6d2e912 100644
--- a/lib/crates/fabro-cli/src/commands/run/command.rs
+++ b/lib/crates/fabro-cli/src/commands/run/command.rs
@@ -7,7 +7,7 @@ use fabro_util::terminal::Styles;
use crate::args::RunArgs;
use crate::command_context::CommandContext;
use crate::shared::print_json_pretty;
-use crate::user_config::settings_layer_with_storage_dir;
+use crate::user_config::load_settings_with_storage_dir;
pub(crate) async fn execute(
mut args: RunArgs,
@@ -17,7 +17,7 @@ pub(crate) async fn execute(
) -> Result<()> {
let styles: &'static Styles = Box::leak(Box::new(Styles::detect_stderr()));
let ctx = CommandContext::for_target(&args.target, printer, cli.clone(), cli_layer)?;
- let cli_defaults = settings_layer_with_storage_dir(None)?;
+ let cli_defaults = load_settings_with_storage_dir(None)?;
args.verbose = args.verbose || cli.output.verbosity == OutputVerbosity::Verbose;
let quiet = args.detach;
diff --git a/lib/crates/fabro-cli/src/commands/run/mod.rs b/lib/crates/fabro-cli/src/commands/run/mod.rs
index 66a83c82c..da1515b2b 100644
--- a/lib/crates/fabro-cli/src/commands/run/mod.rs
+++ b/lib/crates/fabro-cli/src/commands/run/mod.rs
@@ -8,7 +8,7 @@ use crate::args::{AttachArgs, RunCommands, RunWorkerArgs, StartArgs};
use crate::command_context::CommandContext;
use crate::server_runs::ServerSummaryLookup;
use crate::shared::print_json_pretty;
-use crate::user_config::settings_layer_with_storage_dir;
+use crate::user_config::load_settings_with_storage_dir;
pub(crate) mod attach;
pub(crate) mod command;
@@ -39,7 +39,7 @@ pub(crate) async fn dispatch(
RunCommands::Run(args) => Box::pin(command::execute(args, cli, cli_layer, printer)).await,
RunCommands::Create(args) => {
let styles: &'static Styles = Box::leak(Box::new(Styles::detect_stderr()));
- let cli_defaults = settings_layer_with_storage_dir(None)?;
+ let cli_defaults = load_settings_with_storage_dir(None)?;
let ctx = CommandContext::for_target(&args.target, printer, cli.clone(), cli_layer)?;
let created_run = Box::pin(create::create_run(
&ctx,
diff --git a/lib/crates/fabro-cli/src/commands/server/foreground.rs b/lib/crates/fabro-cli/src/commands/server/foreground.rs
index 390dc4a38..f2b536281 100644
--- a/lib/crates/fabro-cli/src/commands/server/foreground.rs
+++ b/lib/crates/fabro-cli/src/commands/server/foreground.rs
@@ -15,7 +15,7 @@ pub(crate) async fn execute(
record_path: PathBuf,
mut serve_args: ServeArgs,
bind: BindRequest,
- storage_dir: Option,
+ storage_dir: PathBuf,
styles: &'static Styles,
printer: Printer,
) -> Result<()> {
@@ -35,22 +35,14 @@ pub(crate) async fn execute(
None
};
- let log_path = storage_dir.as_ref().map_or_else(
- || {
- record_path.parent().map_or_else(
- || PathBuf::from("server.log"),
- |parent| parent.join("server.log"),
- )
- },
- |dir| Storage::new(dir).server_state().log_path(),
- );
+ let log_path = Storage::new(&storage_dir).server_state().log_path();
let dev_token_path = std::env::var_os("FABRO_DEV_TOKEN_PATH").map(PathBuf::from);
let pid = std::process::id();
Box::pin(serve::serve_command(
serve_args,
styles,
- storage_dir,
+ Some(storage_dir),
move |resolved_bind| {
record::write_server_record(&record_path, &record::ServerRecord {
pid,
diff --git a/lib/crates/fabro-cli/src/commands/server/mod.rs b/lib/crates/fabro-cli/src/commands/server/mod.rs
index 77addbaae..eb8cfe6fb 100644
--- a/lib/crates/fabro-cli/src/commands/server/mod.rs
+++ b/lib/crates/fabro-cli/src/commands/server/mod.rs
@@ -9,7 +9,7 @@ use std::time::Duration;
use anyhow::Result;
use base64::Engine as _;
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
-use fabro_config::user::{FABRO_CONFIG_ENV, active_settings_path, legacy_default_storage_root};
+use fabro_config::user::{FABRO_CONFIG_ENV, active_settings_path, default_storage_dir};
use fabro_server::bind::{self, Bind, BindRequest};
use fabro_server::install::{self, InstallAppState};
use fabro_server::serve::{self, ServeArgs};
@@ -27,6 +27,7 @@ use crate::user_config;
pub(crate) async fn dispatch(
command: ServerCommand,
_globals: &GlobalArgs,
+ foreground_log_bootstrap: Option,
printer: Printer,
) -> Result<()> {
match command {
@@ -62,6 +63,7 @@ pub(crate) async fn dispatch(
foreground,
serve_args,
storage_dir,
+ foreground_log_bootstrap,
styles,
printer,
))
@@ -73,8 +75,7 @@ pub(crate) async fn dispatch(
}) => {
let settings = user_config::load_settings_with_storage_dir(storage_dir.as_deref())?;
let storage_dir = user_config::storage_dir(&settings)?;
- stop::execute(&storage_dir, Duration::from_secs(timeout), printer).await;
- Ok(())
+ stop::execute(&storage_dir, Duration::from_secs(timeout), printer).await
}
ServerCommand::Restart(ServerRestartArgs {
storage_dir,
@@ -87,7 +88,7 @@ pub(crate) async fn dispatch(
storage_dir.as_deref(),
&serve_args,
)? {
- stop::stop_server(&bootstrap.storage_dir, Duration::from_secs(timeout)).await;
+ stop::stop_server(&bootstrap.storage_dir, Duration::from_secs(timeout)).await?;
if serve_args.no_web {
fabro_util::printerr!(
printer,
@@ -102,7 +103,7 @@ pub(crate) async fn dispatch(
storage_dir.as_deref(),
)?;
let storage_dir = user_config::storage_dir(&settings)?;
- stop::stop_server(&storage_dir, Duration::from_secs(timeout)).await;
+ stop::stop_server(&storage_dir, Duration::from_secs(timeout)).await?;
let bind_addr =
serve::resolve_bind_request_from_settings(&settings, serve_args.bind.as_deref())?;
let styles: &'static Styles = Box::leak(Box::new(Styles::detect_stderr()));
@@ -111,6 +112,7 @@ pub(crate) async fn dispatch(
foreground,
serve_args,
storage_dir,
+ foreground_log_bootstrap,
styles,
printer,
))
@@ -136,6 +138,7 @@ pub(crate) async fn dispatch(
.clone()
.unwrap_or_else(|| user_config::active_settings_path(None)),
);
+ let storage_dir = user_config::storage_dir(&settings)?;
let bind_addr =
serve::resolve_bind_request_from_settings(&settings, serve_args.bind.as_deref())?;
let styles: &'static Styles = Box::leak(Box::new(Styles::detect_stderr()));
@@ -146,7 +149,7 @@ pub(crate) async fn dispatch(
..serve_args
},
bind_addr,
- storage_dir.clone_path(),
+ storage_dir,
styles,
printer,
))
@@ -181,10 +184,7 @@ fn maybe_install_bootstrap(
None => default_install_bind_request(),
};
- let storage_dir = storage_dir.map_or_else(
- || legacy_default_storage_root().join("storage"),
- std::path::Path::to_path_buf,
- );
+ let storage_dir = storage_dir.map_or_else(default_storage_dir, std::path::Path::to_path_buf);
Ok(Some(InstallBootstrap {
bind_request,
diff --git a/lib/crates/fabro-cli/src/commands/server/record.rs b/lib/crates/fabro-cli/src/commands/server/record.rs
index 560f9d9f7..1a6b15b5a 100644
--- a/lib/crates/fabro-cli/src/commands/server/record.rs
+++ b/lib/crates/fabro-cli/src/commands/server/record.rs
@@ -1,10 +1,10 @@
use std::path::{Path, PathBuf};
-use anyhow::{Context, Result};
+use anyhow::{Context, Result, bail};
use chrono::{DateTime, Utc};
use fabro_config::Storage;
-use fabro_config::user::legacy_default_storage_root;
use fabro_server::bind::Bind;
+use fabro_util::Home;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -50,9 +50,8 @@ fn server_record_path(storage_dir: &Path) -> PathBuf {
}
fn legacy_record_path(storage_dir: &Path) -> Option {
- let default_storage_dir = legacy_default_storage_root().join("storage");
- if storage_dir == default_storage_dir {
- Some(server_record_path(&legacy_default_storage_root()))
+ if storage_dir == fabro_config::user::default_storage_dir() {
+ Some(Home::from_env().root().join("server.json"))
} else {
None
}
@@ -71,14 +70,29 @@ fn active_server_record_at_path(path: PathBuf) -> Option {
}
}
-pub(crate) fn active_server_record_details(storage_dir: &Path) -> Option {
+pub(crate) fn active_server_record_details(
+ storage_dir: &Path,
+) -> Result