Skip to content

feat(core): add tlsRejectUnauthorized option to Context for dev environments - #141

Closed
birme wants to merge 2 commits into
mainfrom
fix/tls-reject-unauthorized-option
Closed

birme wants to merge 2 commits into
mainfrom
fix/tls-reject-unauthorized-option

Conversation

@birme

@birme birme commented Jun 4, 2026

Copy link
Copy Markdown
Member

Summary

Adds tlsRejectUnauthorized?: boolean to ContextConfig (defaults to true). When false, fetch calls to instance health endpoints bypass TLS certificate verification — for use in dev workspaces where instances serve self-signed certs.

const ctx = new Context({ personalAccessToken: '...', tlsRejectUnauthorized: false });
await waitForInstanceReady(ctx, service, instanceName);

Fixes: Eyevinn/osaas-token-service#71

Test plan

  • Unit tests: tlsRejectUnauthorized defaults to true, accepts false
  • Manual: waitForInstanceReady() succeeds against oscaidev instance with self-signed cert

Lessons

Node.js native fetch (undici-based) does not expose dispatcher in TypeScript's RequestInit type. Use require('undici') with a minimal type cast for Agent, then cast fetch init to any to pass the dispatcher. This avoids adding undici as an explicit dependency.

🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com

…onments

Allows bypassing TLS certificate verification for oscaidev workspace
instances that serve self-signed certs. Defaults to true (no change for
existing users).

Co-Authored-By: Claude <noreply@anthropic.com>
@birme birme self-assigned this Jun 4, 2026
@birme

birme commented Jun 4, 2026

Copy link
Copy Markdown
Member Author

Research Review

Verified

  • ContextConfig interface and Context class structure: tlsRejectUnauthorized?: boolean added to ContextConfig at context.ts:6. Private field _tlsRejectUnauthorized: boolean at context.ts:29, initialized in constructor at lines 42–43, exposed via getter at lines 54–56. All match the diff exactly — structure verified against the actual source file.

  • Constructor default logic: config?.tlsRejectUnauthorized !== false ? true : false correctly defaults to true when the option is undefined (not provided). Verified in source.

  • dispatcher property with undici Agent: This is the correct mechanism for overriding TLS behavior on Node.js native fetch. The dispatcher option accepts an undici Agent instance, and connect: { rejectUnauthorized: false } is the correct nested shape for TLS options on undici's Agent. Confirmed against official undici documentation and issue tracker (see sources below).

  • Scoping of tlsRejectUnauthorized to health endpoint only: Only getInstanceHealth() (core.ts:301–312) passes the fourth argument to createFetch. All other createFetch calls in context.ts (catalog/token-service calls) and the remaining calls in core.ts (createInstance, removeInstance, getInstance, getService, getPortsForInstance, getLogsForInstance, restartInstance, updateInstance, etc.) use the 3-argument form, which defaults tlsRejectUnauthorized = true. The getInstanceLink helper calls getInstance and getService without the flag — correct, since those target OSC platform APIs (not the self-signed instance health endpoint).

  • createFetch signature change is backward-compatible: Adding tlsRejectUnauthorized = true as a fourth parameter with a default value does not break any existing callsite. Verified by scanning all 20+ createFetch calls in the repo.

  • undici version compatibility: undici 5.28.4 is present in node_modules. The Agent({ connect: { rejectUnauthorized: false } }) constructor confirmed to instantiate correctly against this version.

Potential Guesses Detected

  • packages/core/package.json — undici is not a declared runtime dependency: undici is present in node_modules only because it is a transitive dev dependency (pulled in by Jest/ts-jest). It is not listed in packages/core/package.json's dependencies field. When @osaas/client-core is published to npm and a consumer installs it in a clean environment, require('undici') at runtime will throw MODULE_NOT_FOUND for any caller that sets tlsRejectUnauthorized: false. This means the feature will always fail for the intended use case (dev environments bypassing self-signed certs) unless the consumer happens to have undici installed independently.

    Fix options (pick one):

    1. Add undici to dependencies in packages/core/package.json.
    2. Guard the require('undici') with a try/catch that throws a clear error: "tlsRejectUnauthorized: false requires 'undici' to be installed as a peer dependency".
    3. Use Node.js's built-in https.Agent + a node-fetch shim, or restructure the option to avoid a dynamic require altogether.

    Node.js 18+ ships with undici built-in under the hood for its native fetch, but require('undici') imports the npm package — not the Node.js internal. These are separate. The fact that node -e "require('undici')" works locally is because undici is a dev dep in this monorepo's root, not because it will be available to downstream consumers.

Adjacent Stubs Detected

None found in touched files.

Orphaned Files Detected

All touched files (context.ts, core.ts, fetch.ts, core.test.ts) are existing files with established importers. No orphaned files introduced.

Impact Analysis

  • Blast radius: createFetch signature change is additive (new optional fourth param with default true). No existing callers are affected. ContextConfig and Context type changes are additive. Consumers of @osaas/client-core that do not pass tlsRejectUnauthorized see identical behavior.
  • Downstream consumers: Many generated service wrappers in packages/services/src/generated/ call waitForInstanceReady(serviceId, name, ctx) — they will silently inherit the new health-check TLS behavior based on whatever ctx was constructed with. This is the intended design (Context carries the config). No coordinated changes needed for those callers.
  • Coordinated changes needed: If undici is added to packages/core/package.json, a version bump and publish of @osaas/client-core will be needed before consumers can use the feature.
  • Breaking change risk: Low for existing callers. Medium for new feature functionality — the missing runtime dependency makes the false path non-functional in published packages.

Risks Not Tested

  • Runtime in a clean npm install: The require('undici') path was not tested by installing @osaas/client-core from npm in an environment without undici as a dev dep. The monorepo dev setup masks the missing dependency.
  • Node.js version portability: dispatcher on the native fetch API requires Node.js 18+. Consumers on Node.js 16 (if any) would get an error even with undici installed. The engines field in package.json was not checked to see if Node.js 18+ is enforced.
  • The getInstanceLink helper still makes un-bypassed TLS calls: getInstanceLink calls getInstance and getService, which fetch from the OSC platform API (catalog, deploy-manager). These will still fail with self-signed cert errors if the broader dev environment also has non-standard TLS on the OSC platform side (unlikely for the stated use case, but not tested).
  • waitForInstanceReady retry loop timeout: The function has no maximum retry count or timeout — it will loop indefinitely if the health endpoint is unreachable. Not introduced by this PR but activated more frequently in the self-signed-cert use case.
  • Live self-signed cert environment: The manual test plan item ("Manual: waitForInstanceReady() succeeds against oscaidev instance with self-signed cert") was not verifiable from diff review.

Research Suggestions

  • Before merging, add undici to dependencies in packages/core/package.json (or add a try/catch with a clear error message), then verify by running npm pack on the core package and npm install in a fresh project without undici installed.
  • Consider adding an integration test that mocks require('undici') to throw (simulating a missing dependency) to ensure the error path is handled gracefully rather than crashing with an opaque MODULE_NOT_FOUND.
  • Check whether engines: { node: ">=18" } is set in packages/core/package.json to match the Node.js native fetch requirement.

Reviewed by PR Research Validator. This review checks whether code is based on verified sources rather than assumptions.

Sources:

require('undici') is used at runtime when tlsRejectUnauthorized: false.
Without this, published @osaas/client-core would throw MODULE_NOT_FOUND
in consumer environments where undici is not a transitive dev dep.

Co-Authored-By: Claude <noreply@anthropic.com>
@birme

birme commented Jun 4, 2026

Copy link
Copy Markdown
Member Author

@birme Fixed: added undici to packages/core/package.json dependencies so the runtime import works for npm consumers in clean environments.

The fix is already committed and pushed as 1b42d23 — undici: "^5.4.0" is now listed under dependencies (not just a transitive dev dep). The require('undici') call in fetch.ts is covered at runtime.

🤖 Generated with Claude Code

Co-Authored-By: Claude noreply@anthropic.com

@birme

birme commented Jun 5, 2026

Copy link
Copy Markdown
Member Author

@birme PR ready to merge — research review approved, undici dependency fix committed (undici added to package.json dependencies), CI type check passing. PR is CLEAN and MERGEABLE. Adds tlsRejectUnauthorized option to Context for dev environments.

🤖 Generated with Claude Code

Co-Authored-By: Claude noreply@anthropic.com

@birme birme closed this Jun 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant