Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 13 additions & 3 deletions src/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
19 changes: 16 additions & 3 deletions test/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
});
});