diff --git a/CHANGELOG.md b/CHANGELOG.md index ea52da7..b088441 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). +## [Unreleased] + +### Changed + +- **Breaking:** `ClientOptions.insecure` now defaults to `false` (TLS). Previously defaulted to `true` (plaintext). Connections to TLS endpoints now work correctly out of the box; plaintext requires explicit `insecure: true`. + +### Added + +- Warning logged when `insecure: true` is set alongside a bearer token, since the token would be transmitted in cleartext. + ## [0.2.0-alpha.1] - 2026-04-16 ### Changed diff --git a/src/channel.ts b/src/channel.ts index 51cb3ee..b724e40 100644 --- a/src/channel.ts +++ b/src/channel.ts @@ -10,12 +10,22 @@ import type { ClientOptions } from "./types.js"; /** * Create gRPC channel credentials based on client options. * - * - If `insecure` is true (default), returns insecure credentials (plaintext). - * - Otherwise, returns TLS credentials. + * - If `insecure` is true, returns insecure credentials (plaintext). + * - Otherwise (default), returns TLS credentials. + * + * Logs a warning when plaintext is enabled with a token configured, + * since the bearer token would be transmitted in cleartext. */ export function createChannel(options: ClientOptions): ChannelCredentials { - const insecure = options.insecure ?? true; + const insecure = options.insecure ?? false; if (insecure) { + if (options.token) { + console.warn( + "[decree] WARNING: insecure=true with a bearer token configured — " + + "the token will be transmitted in cleartext. " + + "Set insecure=false (or omit it) to use TLS.", + ); + } return credentials.createInsecure(); } return credentials.createSsl(); diff --git a/src/types.ts b/src/types.ts index d9edff4..b27d44d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -59,7 +59,7 @@ export interface ClientOptions { readonly tenantId?: string; /** Bearer token. When set, metadata headers are not sent. */ readonly token?: string; - /** Use plaintext (no TLS). Default: true. */ + /** Use plaintext (no TLS). Default: false. Set to true only for local/dev servers without TLS. */ readonly insecure?: boolean; /** Default per-RPC timeout in milliseconds. Default: 10000. */ readonly timeout?: number; diff --git a/test/client.test.ts b/test/client.test.ts index b54a1ac..778f853 100644 --- a/test/client.test.ts +++ b/test/client.test.ts @@ -421,14 +421,27 @@ describe("ConfigClient", () => { }); describe("TLS channel", () => { - it("creates insecure channel by default", () => { + it("creates TLS channel by default", () => { const c = new ConfigClient("localhost:9090", { retry: false }); c.close(); }); - it("creates TLS channel when insecure is false", () => { - const c = new ConfigClient("localhost:9090", { insecure: false, retry: false }); + it("creates insecure channel when insecure is true", () => { + const c = new ConfigClient("localhost:9090", { insecure: true, retry: false }); c.close(); }); + + it("warns when insecure is true and a token is configured", () => { + const warn = vi.spyOn(console, "warn").mockImplementation(() => {}); + const c = new ConfigClient("localhost:9090", { + insecure: true, + token: "secret", + retry: false, + }); + c.close(); + expect(warn).toHaveBeenCalledOnce(); + expect(warn.mock.calls[0][0]).toContain("cleartext"); + warn.mockRestore(); + }); }); });