From af7a2b81fac18c6b4f3f7e352ab0a54efebf7339 Mon Sep 17 00:00:00 2001 From: Einar Date: Fri, 10 Jul 2026 13:55:16 +0200 Subject: [PATCH 1/4] Bump Chronicle client packages to 16.0.1 for the single-port Kernel Chronicle's Kernel consolidated gRPC and OAuth/HTTP traffic onto a single TLS port (35000), removing the separate management port (8080). 16.0.1 is the first Cratis.Chronicle.Connections/Contracts/XUnit.Integration release with the updated client APIs (OAuthTokenProvider no longer takes a management port). Testcontainers is bumped alongside it to satisfy the higher minimum version now required by Cratis.Chronicle.XUnit.Integration. --- Directory.Packages.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 2951623..7890d4a 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,8 +5,8 @@ - - + + @@ -30,7 +30,7 @@ - + From 1e781c6110a3a8848089866935602ac01eeacedc Mon Sep 17 00:00:00 2001 From: Einar Date: Fri, 10 Jul 2026 13:55:26 +0200 Subject: [PATCH 2/4] Remove management port and require TLS by default in the CLI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Chronicle's Kernel now serves gRPC and the OAuth token endpoint on a single TLS port instead of a separate HTTP management port. Derive the OAuth token endpoint from the connection string's server address instead of a configured management port, and drop --management-port, CHRONICLE_MANAGEMENT_PORT, and the context management-port key entirely. Default connection strings no longer disable TLS — the CLI now connects over TLS by default and, via the bumped client package, automatically trusts the server's self-signed development certificate, matching the .NET client's behavior. --- Source/Cli/CliContext.cs | 5 --- .../Commands/Chronicle/Auth/LoginCommand.cs | 3 +- .../Commands/Chronicle/ChronicleCommand.cs | 17 ++++------ .../Commands/Chronicle/ChronicleSettings.cs | 32 +------------------ .../Chronicle/CliChronicleConnection.cs | 15 ++++----- Source/Cli/Commands/Chronicle/CliDefaults.cs | 10 ------ .../Chronicle/EventStoreInterceptor.cs | 3 +- .../Commands/Chronicle/EventStoreSelector.cs | 4 +-- .../Commands/Completions/CliCommandTree.cs | 1 - .../Completions/FishCompletionGenerator.cs | 1 - .../Completions/ZshCompletionGenerator.cs | 1 - .../Commands/Context/CreateContextCommand.cs | 2 +- .../Cli/Commands/Context/SetValueCommand.cs | 16 ++-------- .../Cli/Commands/Context/SetValueSettings.cs | 2 +- .../Commands/GetStarted/GetStartedCommand.cs | 7 ++-- .../Commands/Init/ChronicleDocGenerator.cs | 2 +- Source/Cli/Commands/Version/VersionCommand.cs | 3 +- Source/Cli/FirstRunDetector.cs | 2 +- Source/Cli/Program.cs | 2 +- 19 files changed, 28 insertions(+), 100 deletions(-) diff --git a/Source/Cli/CliContext.cs b/Source/Cli/CliContext.cs index 6523a55..66bef49 100644 --- a/Source/Cli/CliContext.cs +++ b/Source/Cli/CliContext.cs @@ -33,11 +33,6 @@ public class CliContext /// public string? ClientSecret { get; set; } - /// - /// Gets or sets the management port for the HTTP API and token endpoint. - /// - public int? ManagementPort { get; set; } - /// /// Gets or sets the cached access token from a previous login. /// diff --git a/Source/Cli/Commands/Chronicle/Auth/LoginCommand.cs b/Source/Cli/Commands/Chronicle/Auth/LoginCommand.cs index c0d8555..516eba1 100644 --- a/Source/Cli/Commands/Chronicle/Auth/LoginCommand.cs +++ b/Source/Cli/Commands/Chronicle/Auth/LoginCommand.cs @@ -46,8 +46,7 @@ protected override async Task ExecuteAsync(CommandContext context, LoginSet var connectionString = new ChronicleConnectionString(settings.ResolveConnectionString()); var disableTls = connectionString.DisableTls; var scheme = disableTls ? "http" : "https"; - var managementPort = settings.ResolveManagementPort(); - var tokenEndpoint = $"{scheme}://{connectionString.ServerAddress.Host}:{managementPort}/connect/token"; + var tokenEndpoint = $"{scheme}://{connectionString.ServerAddress.Host}:{connectionString.ServerAddress.Port}/connect/token"; using var handler = CreateHandler(); using var httpClient = new HttpClient(handler); diff --git a/Source/Cli/Commands/Chronicle/ChronicleCommand.cs b/Source/Cli/Commands/Chronicle/ChronicleCommand.cs index 2ac9890..58b0883 100644 --- a/Source/Cli/Commands/Chronicle/ChronicleCommand.cs +++ b/Source/Cli/Commands/Chronicle/ChronicleCommand.cs @@ -40,8 +40,7 @@ protected sealed override async Task ExecuteAsync(CommandContext context, T try { var connectionString = new ChronicleConnectionString(settings.ResolveConnectionString()); - var managementPort = settings.ResolveManagementPort(); - using var client = await CliChronicleConnection.Connect(connectionString, managementPort, cancellationToken); + using var client = await CliChronicleConnection.Connect(connectionString, cancellationToken); int exitCode; var sw = settings.Debug ? Stopwatch.StartNew() : null; @@ -125,18 +124,16 @@ static void WriteDebugInfo(ChronicleSettings settings) var configPath = CliConfiguration.GetConfigPath(); var config = CliConfiguration.Load(); var connectionString = settings.ResolveConnectionString(); - var managementPort = settings.ResolveManagementPort(); - Console.Error.WriteLine($"[debug] config: {configPath}"); - Console.Error.WriteLine($"[debug] context: {config.ActiveContextName}"); - Console.Error.WriteLine($"[debug] server: {RedactConnectionString(connectionString)}"); - Console.Error.WriteLine($"[debug] management-port: {managementPort}"); - Console.Error.WriteLine($"[debug] output: {settings.ResolveOutputFormat()}"); + Console.Error.WriteLine($"[debug] config: {configPath}"); + Console.Error.WriteLine($"[debug] context: {config.ActiveContextName}"); + Console.Error.WriteLine($"[debug] server: {RedactConnectionString(connectionString)}"); + Console.Error.WriteLine($"[debug] output: {settings.ResolveOutputFormat()}"); if (settings is EventStoreSettings ess) { - Console.Error.WriteLine($"[debug] event-store: {ess.ResolveEventStore()}"); - Console.Error.WriteLine($"[debug] namespace: {ess.ResolveNamespace()}"); + Console.Error.WriteLine($"[debug] event-store: {ess.ResolveEventStore()}"); + Console.Error.WriteLine($"[debug] namespace: {ess.ResolveNamespace()}"); } } diff --git a/Source/Cli/Commands/Chronicle/ChronicleSettings.cs b/Source/Cli/Commands/Chronicle/ChronicleSettings.cs index a73ed3f..5fb2ad3 100644 --- a/Source/Cli/Commands/Chronicle/ChronicleSettings.cs +++ b/Source/Cli/Commands/Chronicle/ChronicleSettings.cs @@ -15,13 +15,6 @@ public class ChronicleSettings : GlobalSettings [Description("Chronicle server connection string (e.g. chronicle://localhost:35000)")] public string? Server { get; set; } - /// - /// Gets or sets the management port for the HTTP API and token endpoint. - /// - [CommandOption("--management-port ")] - [Description("Management port for the HTTP API and token endpoint (default: 8080)")] - public int? ManagementPort { get; set; } - /// /// Resolves the effective connection string by checking flag, environment variable, current context, then default. /// When the resolved connection string has no embedded credentials, client credentials from the context are composed in. @@ -48,36 +41,13 @@ public string ResolveConnectionString() var ctx = config.GetCurrentContext(); connectionString = !string.IsNullOrWhiteSpace(ctx.Server) ? ctx.Server - : "chronicle://localhost:35000/?disableTls=true"; + : "chronicle://localhost:35000"; } } return ComposeCredentials(connectionString); } - /// - /// Resolves the effective management port by checking flag, environment variable, current context, then default. - /// - /// The resolved management port. - public int ResolveManagementPort() - { - if (ManagementPort.HasValue) - { - return ManagementPort.Value; - } - - var envVar = Environment.GetEnvironmentVariable(CliDefaults.ManagementPortEnvVar); - if (!string.IsNullOrWhiteSpace(envVar) && int.TryParse(envVar, out var envPort)) - { - return envPort; - } - - var config = CliConfiguration.Load(); - var ctx = config.GetCurrentContext(); - - return ctx.ManagementPort ?? CliDefaults.DefaultManagementPort; - } - static string ComposeCredentials(string connectionString) { // Avoid calling the ChronicleConnectionString constructor here — it throws when no diff --git a/Source/Cli/Commands/Chronicle/CliChronicleConnection.cs b/Source/Cli/Commands/Chronicle/CliChronicleConnection.cs index 5d1fecb..32eca0b 100644 --- a/Source/Cli/Commands/Chronicle/CliChronicleConnection.cs +++ b/Source/Cli/Commands/Chronicle/CliChronicleConnection.cs @@ -22,10 +22,9 @@ public sealed class CliChronicleConnection(ChronicleConnection connection, Cance /// Creates and connects a from a connection string. /// /// The parsed Chronicle connection string. - /// The management port for the token endpoint. /// Cancellation token. /// A connected . - public static async Task Connect(ChronicleConnectionString connectionString, int managementPort, CancellationToken cancellationToken = default) + public static async Task Connect(ChronicleConnectionString connectionString, CancellationToken cancellationToken = default) { #pragma warning disable CA2000 // cts and connection ownership is transferred to CliChronicleConnection on success; both are disposed in the catch block on failure var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); @@ -38,7 +37,7 @@ public static async Task Connect(ChronicleConnectionStri var config = CliConfiguration.Load(); var tokenProvider = connectionString.AuthenticationMode switch { - AuthenticationMode.ClientCredentials => (ITokenProvider?)CreateCachingTokenProvider(connectionString, managementPort, config.ActiveContextName), + AuthenticationMode.ClientCredentials => (ITokenProvider?)CreateCachingTokenProvider(connectionString, config.ActiveContextName), AuthenticationMode.ApiKey when !string.IsNullOrEmpty(connectionString.ApiKey) => new StaticTokenProvider(connectionString.ApiKey), _ => null @@ -66,7 +65,7 @@ public static async Task Connect(ChronicleConnectionStri skipCompatibilityCheck: true, skipKeepAlive: true); - // Eagerly fetch the token so HttpRequestException from an unreachable management port + // Eagerly fetch the token so HttpRequestException from an unreachable server // propagates directly instead of being wrapped as RpcException by the gRPC interceptor. if (tokenProvider is not null) { @@ -91,11 +90,10 @@ public static async Task Connect(ChronicleConnectionStri /// blocking on async here is intentional and safe because this is always called from a thread-pool context without a synchronization context. /// /// The parsed Chronicle connection string. - /// The management port for the token endpoint. /// A connected . #pragma warning disable CA2000 // Caller is responsible for disposing the returned instance - public static CliChronicleConnection ConnectSync(ChronicleConnectionString connectionString, int managementPort) - => Connect(connectionString, managementPort).GetAwaiter().GetResult(); + public static CliChronicleConnection ConnectSync(ChronicleConnectionString connectionString) + => Connect(connectionString).GetAwaiter().GetResult(); #pragma warning restore CA2000 /// @@ -122,7 +120,7 @@ public void Dispose() connection.Dispose(); } - static FileSystemCachingTokenProvider CreateCachingTokenProvider(ChronicleConnectionString connectionString, int managementPort, string contextName) + static FileSystemCachingTokenProvider CreateCachingTokenProvider(ChronicleConnectionString connectionString, string contextName) { var cachePath = CliConfiguration.GetTokenCachePath($"{contextName}_{connectionString.Username ?? string.Empty}"); Directory.CreateDirectory(Path.GetDirectoryName(cachePath)!); @@ -131,7 +129,6 @@ static FileSystemCachingTokenProvider CreateCachingTokenProvider(ChronicleConnec connectionString.ServerAddress, connectionString.Username ?? string.Empty, connectionString.Password ?? string.Empty, - managementPort, connectionString.DisableTls, NullLogger.Instance); #pragma warning restore CA2000 diff --git a/Source/Cli/Commands/Chronicle/CliDefaults.cs b/Source/Cli/Commands/Chronicle/CliDefaults.cs index e73a8e6..7b53277 100644 --- a/Source/Cli/Commands/Chronicle/CliDefaults.cs +++ b/Source/Cli/Commands/Chronicle/CliDefaults.cs @@ -23,21 +23,11 @@ public static class CliDefaults /// public const string DefaultNamespaceName = "Default"; - /// - /// The default management port. - /// - public const int DefaultManagementPort = 8080; - /// /// Environment variable name for the Chronicle connection string. /// public const string ConnectionStringEnvVar = "CHRONICLE_CONNECTION_STRING"; - /// - /// Environment variable name for the management port override. - /// - public const string ManagementPortEnvVar = "CHRONICLE_MANAGEMENT_PORT"; - /// /// Standard error message when the CLI cannot reach the Chronicle server. /// diff --git a/Source/Cli/Commands/Chronicle/EventStoreInterceptor.cs b/Source/Cli/Commands/Chronicle/EventStoreInterceptor.cs index d55d350..f136e77 100644 --- a/Source/Cli/Commands/Chronicle/EventStoreInterceptor.cs +++ b/Source/Cli/Commands/Chronicle/EventStoreInterceptor.cs @@ -39,10 +39,9 @@ public void Intercept(CommandContext context, CommandSettings settings) var config = CliConfiguration.Load(); var ctx = config.GetCurrentContext(); var connectionString = new ChronicleConnectionString(eventStoreSettings.ResolveConnectionString()); - var managementPort = eventStoreSettings.ResolveManagementPort(); // Pass the currently stored event store so the selector can validate it is still present. // If it is missing or empty the selector will prompt the user and save the selection. - EventStoreSelector.TryPromptAndSave(connectionString, managementPort, config, ctx, ctx.EventStore); + EventStoreSelector.TryPromptAndSave(connectionString, config, ctx, ctx.EventStore); } } diff --git a/Source/Cli/Commands/Chronicle/EventStoreSelector.cs b/Source/Cli/Commands/Chronicle/EventStoreSelector.cs index bfd42e2..46a8c09 100644 --- a/Source/Cli/Commands/Chronicle/EventStoreSelector.cs +++ b/Source/Cli/Commands/Chronicle/EventStoreSelector.cs @@ -35,7 +35,6 @@ public static class EventStoreSelector /// default selection, and saves the choice to the context in the provided . /// /// The Chronicle connection string to use. - /// The management port. /// The CLI configuration to update and save. /// The current context to update. /// @@ -46,7 +45,6 @@ public static class EventStoreSelector /// An describing the outcome. public static EventStoreSelectorResult TryPromptAndSave( ChronicleConnectionString connectionString, - int managementPort, CliConfiguration config, CliContext ctx, string? currentEventStore = null) @@ -61,7 +59,7 @@ public static EventStoreSelectorResult TryPromptAndSave( { eventStores = Task.Run(async () => { - using var client = await CliChronicleConnection.Connect(connectionString, managementPort); + using var client = await CliChronicleConnection.Connect(connectionString); return (await client.Services.EventStores.GetEventStores()).ToList(); }).GetAwaiter().GetResult(); } diff --git a/Source/Cli/Commands/Completions/CliCommandTree.cs b/Source/Cli/Commands/Completions/CliCommandTree.cs index 41321fa..8fc30be 100644 --- a/Source/Cli/Commands/Completions/CliCommandTree.cs +++ b/Source/Cli/Commands/Completions/CliCommandTree.cs @@ -21,7 +21,6 @@ public static class CliCommandTree "-o", "--output", "-q", "--quiet", "-y", "--yes", - "--management-port", "--debug" ]; diff --git a/Source/Cli/Commands/Completions/FishCompletionGenerator.cs b/Source/Cli/Commands/Completions/FishCompletionGenerator.cs index f5e26f1..e571cf1 100644 --- a/Source/Cli/Commands/Completions/FishCompletionGenerator.cs +++ b/Source/Cli/Commands/Completions/FishCompletionGenerator.cs @@ -25,7 +25,6 @@ public static string Generate() .AppendLine("complete -c cratis -s o -l output -d 'Output format' -r -f -a '(cratis _complete output-formats 2>/dev/null)'") .AppendLine("complete -c cratis -s q -l quiet -d 'Suppress non-essential output'") .AppendLine("complete -c cratis -s y -l yes -d 'Skip confirmation prompts'") - .AppendLine("complete -c cratis -l management-port -d 'Management port' -r") .AppendLine(); var commands = CliCommandTree.Commands; diff --git a/Source/Cli/Commands/Completions/ZshCompletionGenerator.cs b/Source/Cli/Commands/Completions/ZshCompletionGenerator.cs index 1cc4555..68f06e4 100644 --- a/Source/Cli/Commands/Completions/ZshCompletionGenerator.cs +++ b/Source/Cli/Commands/Completions/ZshCompletionGenerator.cs @@ -30,7 +30,6 @@ public static string Generate() .AppendLine(" {-o,--output}'[Output format]:format:(json json-compact plain table)' \\") .AppendLine(" {-q,--quiet}'[Suppress non-essential output]' \\") .AppendLine(" {-y,--yes}'[Skip confirmation prompts]' \\") - .AppendLine(" '--management-port[Management port]:port:' \\") .AppendLine(" '1: :->command' \\") .AppendLine(" '*::arg:->args'") .AppendLine() diff --git a/Source/Cli/Commands/Context/CreateContextCommand.cs b/Source/Cli/Commands/Context/CreateContextCommand.cs index 668808e..b00990c 100644 --- a/Source/Cli/Commands/Context/CreateContextCommand.cs +++ b/Source/Cli/Commands/Context/CreateContextCommand.cs @@ -8,7 +8,7 @@ namespace Cratis.Cli.Commands.Context; /// [LlmDescription("Creates a new named context (server profile) with a connection string. Use to save connection settings for different Chronicle environments.")] [CliCommand("create", "Create a new context", Branch = typeof(ContextBranch))] -[CliExample("context", "create", "dev", "--server", "chronicle://localhost:35000/?disableTls=true")] +[CliExample("context", "create", "dev", "--server", "chronicle://localhost:35000")] [CliExample("context", "create", "prod", "--server", "chronicle://prod:35000", "-e", "production")] [LlmOutputAdvice("plain", "Plain outputs a confirmation message.")] [LlmOption("", "string", "Name of the context to create (positional)")] diff --git a/Source/Cli/Commands/Context/SetValueCommand.cs b/Source/Cli/Commands/Context/SetValueCommand.cs index d272f36..c29f22d 100644 --- a/Source/Cli/Commands/Context/SetValueCommand.cs +++ b/Source/Cli/Commands/Context/SetValueCommand.cs @@ -10,7 +10,7 @@ namespace Cratis.Cli.Commands.Context; [CliCommand("set-value", "Set a value in the current context", Branch = typeof(ContextBranch))] [CliExample("context", "set-value", "server", "chronicle://myhost:35000")] [CliExample("context", "set-value", "client-id", "my-app")] -[LlmOption("", "string", "Key to set: server, event-store, namespace, client-id, client-secret, management-port (positional). These update the active context's defaults.")] +[LlmOption("", "string", "Key to set: server, event-store, namespace, client-id, client-secret (positional). These update the active context's defaults.")] [LlmOption("", "string", "Value to assign (positional)")] public class SetValueCommand : AsyncCommand { @@ -37,21 +37,9 @@ protected override Task ExecuteAsync(CommandContext context, SetValueSettin break; case "client-secret": ctx.ClientSecret = settings.Value; - break; - case "management-port": - if (int.TryParse(settings.Value, out var port)) - { - ctx.ManagementPort = port; - } - else - { - OutputFormatter.WriteError(format, $"Invalid port value: '{settings.Value}'", "management-port must be a valid integer.", ExitCodes.ValidationErrorCode); - return Task.FromResult(ExitCodes.ValidationError); - } - break; default: - OutputFormatter.WriteError(format, $"Unknown context key: '{settings.Key}'", "Valid keys: server, event-store, namespace, client-id, client-secret, management-port", ExitCodes.NotFoundCode); + OutputFormatter.WriteError(format, $"Unknown context key: '{settings.Key}'", "Valid keys: server, event-store, namespace, client-id, client-secret", ExitCodes.NotFoundCode); return Task.FromResult(ExitCodes.NotFound); } diff --git a/Source/Cli/Commands/Context/SetValueSettings.cs b/Source/Cli/Commands/Context/SetValueSettings.cs index baf1bb0..7b38766 100644 --- a/Source/Cli/Commands/Context/SetValueSettings.cs +++ b/Source/Cli/Commands/Context/SetValueSettings.cs @@ -12,7 +12,7 @@ public class SetValueSettings : GlobalSettings /// Gets or sets the configuration key. /// [CommandArgument(0, "")] - [Description("Configuration key (server, event-store, namespace, client-id, client-secret, management-port)")] + [Description("Configuration key (server, event-store, namespace, client-id, client-secret)")] public string Key { get; set; } = string.Empty; /// diff --git a/Source/Cli/Commands/GetStarted/GetStartedCommand.cs b/Source/Cli/Commands/GetStarted/GetStartedCommand.cs index 6f48eb9..9ea451a 100644 --- a/Source/Cli/Commands/GetStarted/GetStartedCommand.cs +++ b/Source/Cli/Commands/GetStarted/GetStartedCommand.cs @@ -45,7 +45,6 @@ protected override async Task ExecuteAsync(CommandContext context, Chronicl var contextName = config.ActiveContextName; var resolvedServer = settings.ResolveConnectionString(); - var managementPort = settings.ResolveManagementPort(); // The display server shown to the user should be the raw context value (without injected credentials). var envServer = Environment.GetEnvironmentVariable(CliDefaults.ConnectionStringEnvVar); @@ -60,7 +59,7 @@ protected override async Task ExecuteAsync(CommandContext context, Chronicl } else { - displayServer = "chronicle://localhost:35000/?disableTls=true"; + displayServer = "chronicle://localhost:35000"; } var authState = ResolveAuthState(ctx); @@ -72,7 +71,7 @@ protected override async Task ExecuteAsync(CommandContext context, Chronicl try { var connectionString = new ChronicleConnectionString(resolvedServer); - using var connection = await CliChronicleConnection.Connect(connectionString, managementPort, cts.Token); + using var connection = await CliChronicleConnection.Connect(connectionString, cts.Token); canConnect = true; } catch @@ -140,7 +139,7 @@ static void RenderNoConfig(string accent, string muted) " No configuration found. Set up a context to get started:\n\n" + $" [{accent}]1.[/] Create a context pointing at your server:\n" + " [bold]cratis context create dev \\\n" + - " --server chronicle://localhost:35000/?disableTls=true[/]\n\n" + + " --server chronicle://localhost:35000[/]\n\n" + $" [{accent}]2.[/] Verify the connection:\n" + " [bold]cratis get-started[/]\n\n" + $" [{accent}]3.[/] Start exploring:\n" + diff --git a/Source/Cli/Commands/Init/ChronicleDocGenerator.cs b/Source/Cli/Commands/Init/ChronicleDocGenerator.cs index 3bc9d09..c62d141 100644 --- a/Source/Cli/Commands/Init/ChronicleDocGenerator.cs +++ b/Source/Cli/Commands/Init/ChronicleDocGenerator.cs @@ -39,7 +39,7 @@ public static string Generate() .AppendLine() .AppendLine("```bash") .AppendLine("# Create a named context") - .AppendLine("cratis context create dev --server chronicle://localhost:35000/?disableTls=true") + .AppendLine("cratis context create dev --server chronicle://localhost:35000") .AppendLine("cratis context set dev") .AppendLine("```") .AppendLine() diff --git a/Source/Cli/Commands/Version/VersionCommand.cs b/Source/Cli/Commands/Version/VersionCommand.cs index a28159a..1243c15 100644 --- a/Source/Cli/Commands/Version/VersionCommand.cs +++ b/Source/Cli/Commands/Version/VersionCommand.cs @@ -41,8 +41,7 @@ protected override async Task ExecuteAsync(CommandContext context, Chronicl try { var connectionString = new ChronicleConnectionString(settings.ResolveConnectionString()); - var managementPort = settings.ResolveManagementPort(); - using var client = await CliChronicleConnection.Connect(connectionString, managementPort, cancellationToken); + using var client = await CliChronicleConnection.Connect(connectionString, cancellationToken); serverInfo = await client.Services.Server.GetVersionInfo(); } catch diff --git a/Source/Cli/FirstRunDetector.cs b/Source/Cli/FirstRunDetector.cs index d173ab3..84ab2b9 100644 --- a/Source/Cli/FirstRunDetector.cs +++ b/Source/Cli/FirstRunDetector.cs @@ -10,7 +10,7 @@ namespace Cratis.Cli; /// public static class FirstRunDetector { - const string DefaultServer = "chronicle://localhost:35000/?disableTls=true"; + const string DefaultServer = "chronicle://localhost:35000"; /// /// Bootstraps a default context when no configuration file is found and prints a welcome message. diff --git a/Source/Cli/Program.cs b/Source/Cli/Program.cs index b2f8a1d..d3ba256 100644 --- a/Source/Cli/Program.cs +++ b/Source/Cli/Program.cs @@ -17,7 +17,7 @@ // This reads from config only — no connection attempt, instant output. var config = CliConfiguration.Load(); var ctx = config.GetCurrentContext(); - var server = ctx.Server ?? "chronicle://localhost:35000/?disableTls=true"; + var server = ctx.Server ?? "chronicle://localhost:35000"; var muted = OutputFormatter.Muted.ToMarkup(); var accent = OutputFormatter.Accent.ToMarkup(); AnsiConsole.MarkupLine($" [{muted}]Context:[/] [{accent}]{config.ActiveContextName.EscapeMarkup()}[/] [{muted}]→[/] {server.EscapeMarkup()}"); From d3d694edfebb37e22a529994ef0328976976f088 Mon Sep 17 00:00:00 2001 From: Einar Date: Fri, 10 Jul 2026 13:55:31 +0200 Subject: [PATCH 3/4] Update CLI integration fixtures for the single Chronicle port Drop the separate 8081->8080 management port mapping and point the health check and OAuth token endpoint at the single TLS port (35001->35000), matching the CLI's connection changes. --- .../ChronicleOutOfProcessFixtureWithLocalImage.cs | 13 ++++++------- Integration/Chronicle/given/a_connected_cli.cs | 4 ++-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Integration/Chronicle/ChronicleOutOfProcessFixtureWithLocalImage.cs b/Integration/Chronicle/ChronicleOutOfProcessFixtureWithLocalImage.cs index cb768dd..acb3788 100644 --- a/Integration/Chronicle/ChronicleOutOfProcessFixtureWithLocalImage.cs +++ b/Integration/Chronicle/ChronicleOutOfProcessFixtureWithLocalImage.cs @@ -18,11 +18,11 @@ namespace Cratis.Cli.Integration.Chronicle; /// /// /// -/// The CLI connects to the Chronicle server externally via gRPC. The management -/// port is mapped to host port 8081 (instead of the default 8080) to avoid -/// conflicts with a locally running Chronicle instance used during development. -/// Tests pass --management-port 8081 to the CLI so that the -/// OAuthTokenProvider reaches https://localhost:8081/connect/token. +/// The CLI connects to the Chronicle server externally via gRPC. The single Chronicle +/// port is mapped to host port 35001 (instead of the default 35000) to avoid conflicts +/// with a locally running Chronicle instance used during development. The OAuth token +/// endpoint is served on that same port, so the OAuthTokenProvider reaches +/// https://localhost:35001/connect/token without any extra configuration. /// /// /// TLS is configured using a self-signed certificate generated at test startup. @@ -78,13 +78,12 @@ public ChronicleOutOfProcessFixtureWithLocalImage() protected override IContainer BuildContainer(INetwork network) { var waitStrategy = Wait.ForUnixContainer() - .AddCustomWaitStrategy(new HttpsHealthWait(8080), s => s.WithTimeout(TimeSpan.FromSeconds(15))); + .AddCustomWaitStrategy(new HttpsHealthWait(35000), s => s.WithTimeout(TimeSpan.FromSeconds(15))); var builder = new ContainerBuilder(ChronicleImageName); builder = ConfigureImage(builder) .WithEnvironment("Storage__ConnectionDetails", "mongodb://localhost:27017") .WithPortBinding(MongoDBPort, 27017) - .WithPortBinding(8081, 8080) .WithPortBinding(35001, 35000) .WithHostname(HostName) .WithBindMount(Path.Combine(Directory.GetCurrentDirectory(), "backups"), "/backups") diff --git a/Integration/Chronicle/given/a_connected_cli.cs b/Integration/Chronicle/given/a_connected_cli.cs index fc56fd7..4df81c2 100644 --- a/Integration/Chronicle/given/a_connected_cli.cs +++ b/Integration/Chronicle/given/a_connected_cli.cs @@ -25,11 +25,11 @@ protected static string ConnectionString /// /// Runs a CLI command against the live server with JSON output format. /// - /// The command arguments (without --server, --management-port, and --output flags). + /// The command arguments (without --server and --output flags). /// The command execution result. protected static Task RunCliAsync(params string[] args) { - var allArgs = new List(args) { "--server", ConnectionString, "--management-port", "8081", "--output", "json" }; + var allArgs = new List(args) { "--server", ConnectionString, "--output", "json" }; return CliCommandRunner.RunAsync([.. allArgs]); } } From 5cfad9809bad8def34e1a84e9516ad532315ede5 Mon Sep 17 00:00:00 2001 From: Einar Date: Fri, 10 Jul 2026 13:55:36 +0200 Subject: [PATCH 4/4] Update documentation for the single Chronicle port Remove management-port flags, keys, and defaults from the README, CHRONICLE.md, and Documentation reference pages. Document that the CLI connects over TLS by default and trusts the server's self-signed development certificate, so no manual TLS setup is needed locally. --- CHRONICLE.md | 2 +- Documentation/chronicle/event-stores.md | 2 +- Documentation/chronicle/index.md | 3 +-- Documentation/context/index.md | 13 +++++---- Documentation/reference/connection.md | 33 +++++++---------------- Documentation/reference/global-options.md | 1 - Documentation/reference/index.md | 2 +- README.md | 32 +++++++++++----------- 8 files changed, 35 insertions(+), 53 deletions(-) diff --git a/CHRONICLE.md b/CHRONICLE.md index 7142ee6..fb5efa9 100644 --- a/CHRONICLE.md +++ b/CHRONICLE.md @@ -18,7 +18,7 @@ The CLI resolves the server connection in this order: ```bash # Set up a named context -cratis context create dev --server chronicle://localhost:35000/?disableTls=true +cratis context create dev --server chronicle://localhost:35000 cratis context set dev ``` diff --git a/Documentation/chronicle/event-stores.md b/Documentation/chronicle/event-stores.md index eeea54d..e0631c4 100644 --- a/Documentation/chronicle/event-stores.md +++ b/Documentation/chronicle/event-stores.md @@ -12,7 +12,7 @@ cratis chronicle event-stores list ### Options -This command accepts only the global flags and the `--server` / `--management-port` connection flags. It does not accept `-e` or `-n` because it operates above the event store level. +This command accepts only the global flags and the `--server` connection flag. It does not accept `-e` or `-n` because it operates above the event store level. ### Output diff --git a/Documentation/chronicle/index.md b/Documentation/chronicle/index.md index c7cbb90..564bc15 100644 --- a/Documentation/chronicle/index.md +++ b/Documentation/chronicle/index.md @@ -4,12 +4,11 @@ The `cratis chronicle` command group provides the primary interface for interact ## Connection Flags -Most commands accept the following connection flags: +Most commands accept the following connection flag: | Flag | Description | |---|---| | `--server ` | Chronicle server connection string. Overrides the active context and the `CHRONICLE_CONNECTION_STRING` environment variable. | -| `--management-port ` | Management API port. Defaults to the port embedded in the connection string. | For the full connection string format, see the [Connection](../reference/connection.md) reference page. diff --git a/Documentation/context/index.md b/Documentation/context/index.md index 46a30e2..2f4ae4f 100644 --- a/Documentation/context/index.md +++ b/Documentation/context/index.md @@ -13,7 +13,7 @@ When the CLI resolves which server to connect to, it checks in this order: 1. `--server` flag on the current command 2. `CHRONICLE_CONNECTION_STRING` environment variable 3. Active context in `~/.cratis/config.json` -4. Default: `chronicle://localhost:35000/?disableTls=true` +4. Default: `chronicle://localhost:35000` ## Commands @@ -34,7 +34,7 @@ cratis context list -o plain Example output: ``` -* dev chronicle://localhost:35000/?disableTls=true +* dev chronicle://localhost:35000 prod chronicle://prod.example.com:35000/ ``` @@ -56,10 +56,10 @@ cratis context create --server | `--event-store` | `-e` | Default event store for this context (default: `default`) | | `--namespace` | `-n` | Default namespace for this context (default: `Default`) | -**Example — local development server with TLS disabled:** +**Example — local development server:** ```bash -cratis context create dev --server chronicle://localhost:35000/?disableTls=true +cratis context create dev --server chronicle://localhost:35000 ``` **Example — staging server with a specific event store and namespace:** @@ -108,7 +108,7 @@ cratis context show -o json Example output (plain): ``` -Server: chronicle://localhost:35000/?disableTls=true +Server: chronicle://localhost:35000 Event store: default Namespace: Default Client ID: (not set) @@ -182,12 +182,11 @@ cratis context set-value | `namespace` | Default namespace name | | `client-id` | OAuth client ID for authenticated servers | | `client-secret` | OAuth client secret (masked in output) | -| `management-port` | HTTP management port (default: `8080`) | **Example — update the server URL:** ```bash -cratis context set-value server chronicle://localhost:35000/?disableTls=true +cratis context set-value server chronicle://localhost:35000 ``` **Example — set credentials for an authenticated server:** diff --git a/Documentation/reference/connection.md b/Documentation/reference/connection.md index 0a53274..369d5ff 100644 --- a/Documentation/reference/connection.md +++ b/Documentation/reference/connection.md @@ -1,6 +1,6 @@ # Connection -This page documents how the `cratis` CLI connects to a Chronicle server: the connection string format, how the server address is resolved, and the management port used for HTTP operations. +This page documents how the `cratis` CLI connects to a Chronicle server: the connection string format and how the server address is resolved. ## Connection String Format @@ -13,15 +13,19 @@ chronicle://:/? **Examples:** ``` -chronicle://localhost:35000/?disableTls=true +chronicle://localhost:35000 chronicle://prod.example.com:35000/ ``` +Chronicle serves gRPC and the OAuth/HTTP API over a single TLS port (default `35000`). TLS is mandatory on that port: in development, the server auto-generates a self-signed certificate for `localhost` and the CLI trusts it automatically — no certificate setup is required to connect to a local server. + ### Options | Option | Values | Description | |---|---|---| -| `disableTls` | `true` / `false` | Disables TLS for the gRPC connection. Required for local servers that do not have a certificate configured. | +| `disableTls` | `true` / `false` | Disables TLS for the connection. Only needed when connecting through a plaintext-terminating proxy — the Chronicle server itself always serves TLS. | +| `certificatePath` | path | Path to a client certificate file, for pinning a specific expected server certificate. | +| `certificatePassword` | string | Password for the certificate file. | ## Resolution Order @@ -30,7 +34,7 @@ When the CLI determines which server to connect to, it checks sources in this or 1. `--server` flag on the current command 2. `CHRONICLE_CONNECTION_STRING` environment variable 3. Active context in `~/.cratis/config.json` -4. Default: `chronicle://localhost:35000/?disableTls=true` +4. Default: `chronicle://localhost:35000` The `--server` flag always wins. The default applies only when none of the other sources provide a value. @@ -45,32 +49,13 @@ cratis chronicle event-stores list The variable is checked after `--server` but before the active context, so a context always overrides it when both are present unless `--server` is used explicitly. -## Management Port - -Some Chronicle operations — including authentication token exchange and certain HTTP management APIs — use a separate HTTP port rather than the gRPC port in the connection string. - -The default management port is `8080`. - -Override it with the `--management-port` flag, which is available on all `cratis` subcommands that communicate with the server: - -```bash -cratis chronicle auth status --management-port 9090 -``` - -You can also persist the management port in a context so you do not need to specify it on every command: - -```bash -cratis context set-value management-port 9090 -``` - ## Per-Command Server Options -Every subcommand that communicates with Chronicle accepts these options directly, which take precedence over context and environment variable values: +Every subcommand that communicates with Chronicle accepts this option directly, which takes precedence over context and environment variable values: | Option | Description | |---|---| | `--server ` | Chronicle server connection string | -| `--management-port ` | HTTP management port (default: `8080`) | **Example — connect to a specific server for a single command without changing the active context:** diff --git a/Documentation/reference/global-options.md b/Documentation/reference/global-options.md index a648444..176edd1 100644 --- a/Documentation/reference/global-options.md +++ b/Documentation/reference/global-options.md @@ -64,7 +64,6 @@ The debug panel includes: - Config file path - Active context name - Connection string (credentials are redacted) -- Management port - Resolved output format - RPC timing for each gRPC call diff --git a/Documentation/reference/index.md b/Documentation/reference/index.md index 6a6c649..68651cc 100644 --- a/Documentation/reference/index.md +++ b/Documentation/reference/index.md @@ -6,4 +6,4 @@ This section documents the cross-cutting mechanics of the `cratis` CLI: the flag - [Global Options](global-options.md) — Flags such as `--output`, `--quiet`, `--yes`, and `--debug` that apply to every command. - [Output Formats](output-formats.md) — The four output formats (`table`, `plain`, `json`, `json-compact`) with guidance on when to use each and token cost comparisons. -- [Connection](connection.md) — Connection string format, resolution order, management port, and environment variable configuration. +- [Connection](connection.md) — Connection string format, resolution order, and environment variable configuration. diff --git a/README.md b/README.md index aa3fa0c..adca920 100644 --- a/README.md +++ b/README.md @@ -104,21 +104,21 @@ cratis get-started If no context is configured yet, the command will walk you through the setup: ``` -╭─ Getting Started ───────────────────────────────────────────────╮ -│ │ -│ No configuration found. Set up a context to get started: │ -│ │ -│ 1. Create a context pointing at your server: │ -│ cratis context create dev \ │ -│ --server chronicle://localhost:35000/?disableTls=true │ -│ │ -│ 2. Verify the connection: │ -│ cratis get-started │ -│ │ -│ 3. Start exploring: │ -│ cratis chronicle event-stores list │ -│ │ -╰─────────────────────────────────────────────────────────────────╯ +╭─ Getting Started ──────────────────────────────────────────╮ +│ │ +│ No configuration found. Set up a context to get started: │ +│ │ +│ 1. Create a context pointing at your server: │ +│ cratis context create dev \ │ +│ --server chronicle://localhost:35000 │ +│ │ +│ 2. Verify the connection: │ +│ cratis get-started │ +│ │ +│ 3. Start exploring: │ +│ cratis chronicle event-stores list │ +│ │ +╰────────────────────────────────────────────────────────────╯ ``` Once a context is configured, `get-started` shows your active context, connection status, and a curated list of commands to explore and debug your event store. @@ -130,7 +130,7 @@ A context stores a named connection to a Chronicle server: ```shell # Create a context for local development cratis context create dev \ - --server chronicle://localhost:35000/?disableTls=true + --server chronicle://localhost:35000 # Make it the active context cratis context set dev