diff --git a/ZB.MOM.WW.Secrets/src/ZB.MOM.WW.Secrets.Cli/Interactive/StoreTarget.cs b/ZB.MOM.WW.Secrets/src/ZB.MOM.WW.Secrets.Cli/Interactive/StoreTarget.cs
new file mode 100644
index 0000000..9721b9e
--- /dev/null
+++ b/ZB.MOM.WW.Secrets/src/ZB.MOM.WW.Secrets.Cli/Interactive/StoreTarget.cs
@@ -0,0 +1,17 @@
+using Microsoft.Extensions.Configuration;
+using ZB.MOM.WW.Secrets.Replicator.SqlServer;
+
+namespace ZB.MOM.WW.Secrets.Cli.Interactive;
+
+/// A resolved deployment store target: the app's composed Secrets options + where they came from.
+/// Display name (defaults to the appsettings parent directory name).
+/// The base appsettings.json path, or for manual-entry targets.
+/// The bound application secrets options, with already made absolute.
+/// The bound SQL-Server hub options when a Secrets:SqlServer section is present; otherwise .
+/// The full composed configuration, consumed later by the reference auditor.
+public sealed record StoreTarget(
+ string Name,
+ string? AppSettingsPath,
+ SecretsOptions Secrets,
+ SqlServerSecretsOptions? SqlServer,
+ IConfigurationRoot Configuration);
diff --git a/ZB.MOM.WW.Secrets/src/ZB.MOM.WW.Secrets.Cli/Interactive/TargetConfigReader.cs b/ZB.MOM.WW.Secrets/src/ZB.MOM.WW.Secrets.Cli/Interactive/TargetConfigReader.cs
new file mode 100644
index 0000000..50949a1
--- /dev/null
+++ b/ZB.MOM.WW.Secrets/src/ZB.MOM.WW.Secrets.Cli/Interactive/TargetConfigReader.cs
@@ -0,0 +1,85 @@
+using Microsoft.Extensions.Configuration;
+using ZB.MOM.WW.Secrets.MasterKey;
+using ZB.MOM.WW.Secrets.Replicator.SqlServer;
+
+namespace ZB.MOM.WW.Secrets.Cli.Interactive;
+
+///
+/// Composes a deployment target's Secrets configuration the same way the target app would at
+/// startup — base appsettings.json, optional per-environment overlay, and environment
+/// variables — so the interactive console operates on exactly the store the app reads.
+///
+public sealed class TargetConfigReader
+{
+ ///
+ /// Loads the target app's config exactly the way the app would (base json + optional environment
+ /// overlay + environment variables) and binds the Secrets options out of it.
+ ///
+ /// Absolute or relative path to the target app's base appsettings.json.
+ ///
+ /// Optional ASP.NET environment name; when non-empty an appsettings.{environment}.json
+ /// overlay is layered on top of the base file (optional — absent overlays are ignored).
+ ///
+ /// The configuration section the Secrets options bind from. Defaults to Secrets.
+ /// The resolved , with the SQLite path made absolute against the app directory.
+ /// The base does not exist.
+ public StoreTarget Read(string appSettingsPath, string? environment = null, string sectionPath = "Secrets")
+ {
+ string fullPath = Path.GetFullPath(appSettingsPath);
+ if (!File.Exists(fullPath))
+ {
+ throw new FileNotFoundException(
+ $"Target appsettings file not found: {fullPath}", fullPath);
+ }
+
+ string dir = Path.GetDirectoryName(fullPath)!;
+ string fileName = Path.GetFileName(fullPath);
+
+ ConfigurationBuilder builder = new();
+ builder.SetBasePath(dir)
+ .AddJsonFile(fileName, optional: false);
+
+ if (!string.IsNullOrEmpty(environment))
+ {
+ builder.AddJsonFile($"appsettings.{environment}.json", optional: true);
+ }
+
+ builder.AddEnvironmentVariables();
+
+ IConfigurationRoot config = builder.Build();
+
+ SecretsOptions secrets = config.GetSection(sectionPath).Get() ?? new SecretsOptions();
+ secrets.SqlitePath = Path.GetFullPath(secrets.SqlitePath, dir);
+
+ SqlServerSecretsOptions? sqlServer = null;
+ IConfigurationSection sqlSection = config.GetSection(sectionPath + ":SqlServer");
+ if (sqlSection.GetChildren().Any())
+ {
+ sqlServer = sqlSection.Get();
+ }
+
+ string name = new DirectoryInfo(dir).Name;
+
+ return new StoreTarget(name, fullPath, secrets, sqlServer, config);
+ }
+
+ ///
+ /// Builds a manual target from an explicit db path + master-key options, with no backing
+ /// appsettings file (for ad-hoc stores the console operates on directly).
+ ///
+ /// Path to the SQLite store; made absolute against the current directory.
+ /// The master-key resolution options for this manual store.
+ /// A named manual with an empty composed configuration and no SQL-Server hub.
+ public StoreTarget Manual(string sqlitePath, MasterKeyOptions masterKey)
+ {
+ SecretsOptions secrets = new()
+ {
+ SqlitePath = Path.GetFullPath(sqlitePath),
+ MasterKey = masterKey,
+ };
+
+ IConfigurationRoot config = new ConfigurationBuilder().Build();
+
+ return new StoreTarget("manual", AppSettingsPath: null, secrets, SqlServer: null, config);
+ }
+}
diff --git a/ZB.MOM.WW.Secrets/tests/ZB.MOM.WW.Secrets.Tests/Cli/Interactive/TargetConfigReaderTests.cs b/ZB.MOM.WW.Secrets/tests/ZB.MOM.WW.Secrets.Tests/Cli/Interactive/TargetConfigReaderTests.cs
new file mode 100644
index 0000000..5682cca
--- /dev/null
+++ b/ZB.MOM.WW.Secrets/tests/ZB.MOM.WW.Secrets.Tests/Cli/Interactive/TargetConfigReaderTests.cs
@@ -0,0 +1,146 @@
+using ZB.MOM.WW.Secrets.Cli.Interactive;
+using ZB.MOM.WW.Secrets.MasterKey;
+
+namespace ZB.MOM.WW.Secrets.Tests.Cli.Interactive;
+
+///
+/// Tests for : it must compose the target app's configuration
+/// (base json + optional environment overlay + environment variables) exactly the way the app
+/// would at startup, so fixes land in the store the app actually reads.
+///
+public sealed class TargetConfigReaderTests
+{
+ private static string NewTempDir()
+ {
+ string dir = Path.Combine(Path.GetTempPath(), "zb-secrets-cfg-" + Guid.NewGuid().ToString("N"));
+ Directory.CreateDirectory(dir);
+ return dir;
+ }
+
+ [Fact]
+ public void Reads_secrets_section_and_binds_options()
+ {
+ string dir = NewTempDir();
+ string path = Path.Combine(dir, "appsettings.json");
+ File.WriteAllText(path, """
+ {
+ "Secrets": {
+ "SqlitePath": "custom.db",
+ "MasterKey": {
+ "Source": "Environment",
+ "EnvVarName": "MY_KEY"
+ }
+ }
+ }
+ """);
+
+ StoreTarget target = new TargetConfigReader().Read(path);
+
+ Assert.Equal(MasterKeySource.Environment, target.Secrets.MasterKey.Source);
+ Assert.Equal("MY_KEY", target.Secrets.MasterKey.EnvVarName);
+ Assert.Equal(Path.Combine(dir, "custom.db"), target.Secrets.SqlitePath);
+ }
+
+ [Fact]
+ public void Environment_overlay_wins_over_base()
+ {
+ string dir = NewTempDir();
+ File.WriteAllText(Path.Combine(dir, "appsettings.json"), """
+ {
+ "Secrets": { "SqlitePath": "base.db" }
+ }
+ """);
+ File.WriteAllText(Path.Combine(dir, "appsettings.Production.json"), """
+ {
+ "Secrets": { "SqlitePath": "prod.db" }
+ }
+ """);
+
+ StoreTarget target = new TargetConfigReader().Read(
+ Path.Combine(dir, "appsettings.json"), environment: "Production");
+
+ Assert.Equal(Path.Combine(dir, "prod.db"), target.Secrets.SqlitePath);
+ }
+
+ [Fact]
+ public void Binds_sqlserver_options_when_section_present()
+ {
+ string dir = NewTempDir();
+ File.WriteAllText(Path.Combine(dir, "appsettings.json"), """
+ {
+ "Secrets": {
+ "SqlitePath": "secrets.db",
+ "SqlServer": {
+ "ConnectionString": "Server=hub;Database=ZbSecrets;Trusted_Connection=True"
+ }
+ }
+ }
+ """);
+
+ StoreTarget withSql = new TargetConfigReader().Read(Path.Combine(dir, "appsettings.json"));
+
+ Assert.NotNull(withSql.SqlServer);
+ Assert.Equal(
+ "Server=hub;Database=ZbSecrets;Trusted_Connection=True",
+ withSql.SqlServer!.ConnectionString);
+
+ string dir2 = NewTempDir();
+ File.WriteAllText(Path.Combine(dir2, "appsettings.json"), """
+ {
+ "Secrets": { "SqlitePath": "secrets.db" }
+ }
+ """);
+
+ StoreTarget noSql = new TargetConfigReader().Read(Path.Combine(dir2, "appsettings.json"));
+
+ Assert.Null(noSql.SqlServer);
+ }
+
+ [Fact]
+ public void SqlitePath_is_resolved_relative_to_the_app_directory()
+ {
+ string dir = NewTempDir();
+ File.WriteAllText(Path.Combine(dir, "appsettings.json"), """
+ {
+ "Secrets": { "SqlitePath": "secrets.db" }
+ }
+ """);
+
+ StoreTarget target = new TargetConfigReader().Read(Path.Combine(dir, "appsettings.json"));
+
+ Assert.True(Path.IsPathRooted(target.Secrets.SqlitePath));
+ Assert.Equal(Path.Combine(dir, "secrets.db"), target.Secrets.SqlitePath);
+ }
+
+ [Fact]
+ public void Missing_file_throws_FileNotFoundException_with_path()
+ {
+ string dir = NewTempDir();
+ string path = Path.Combine(dir, "appsettings.json");
+
+ FileNotFoundException ex = Assert.Throws(
+ () => new TargetConfigReader().Read(path));
+
+ Assert.Contains(path, ex.Message);
+ }
+
+ [Fact]
+ public void Custom_section_path_is_honored()
+ {
+ string dir = NewTempDir();
+ File.WriteAllText(Path.Combine(dir, "appsettings.json"), """
+ {
+ "MySecrets": {
+ "SqlitePath": "elsewhere.db",
+ "MasterKey": { "EnvVarName": "OTHER_KEY" }
+ }
+ }
+ """);
+
+ StoreTarget target = new TargetConfigReader().Read(
+ Path.Combine(dir, "appsettings.json"), sectionPath: "MySecrets");
+
+ Assert.Equal("OTHER_KEY", target.Secrets.MasterKey.EnvVarName);
+ Assert.Equal(Path.Combine(dir, "elsewhere.db"), target.Secrets.SqlitePath);
+ }
+}