feat(federation): outbound mTLS FederationClient (FED-M3-08) #508

Merged
jason.woltje merged 3 commits from feat/federation-m3-client into main 2026-04-24 04:30:30 +00:00
Owner

Summary

Implements FederationClientService — outbound mTLS HTTP client for federation verb requests.

Closes #462

Changes

  • New service at apps/gateway/src/federation/client/federation-client.service.ts
  • Barrel export at apps/gateway/src/federation/client/index.ts
  • FederationModule wired to include FederationClientService in providers + exports
  • undici added as direct dependency to apps/gateway/package.json
  • 13 unit tests covering all required error paths and cache behaviour

Error Code Taxonomy

PEER_NOT_FOUND, PEER_INACTIVE, PEER_MISCONFIGURED, NETWORK, FORBIDDEN, HTTP_{status}, INVALID_RESPONSE

Pre-PR Gates

  • pnpm typecheck: pass
  • pnpm lint: pass
  • pnpm format:check: pass
  • pnpm --filter @mosaicstack/gateway test: 452 passed (13 new)

Generated with Claude Code

## Summary Implements `FederationClientService` — outbound mTLS HTTP client for federation verb requests. Closes #462 ## Changes - New service at `apps/gateway/src/federation/client/federation-client.service.ts` - Barrel export at `apps/gateway/src/federation/client/index.ts` - `FederationModule` wired to include `FederationClientService` in providers + exports - `undici` added as direct dependency to `apps/gateway/package.json` - 13 unit tests covering all required error paths and cache behaviour ## Error Code Taxonomy PEER_NOT_FOUND, PEER_INACTIVE, PEER_MISCONFIGURED, NETWORK, FORBIDDEN, HTTP_{status}, INVALID_RESPONSE ## Pre-PR Gates - pnpm typecheck: pass - pnpm lint: pass - pnpm format:check: pass - pnpm --filter @mosaicstack/gateway test: 452 passed (13 new) Generated with Claude Code
jason.woltje added 1 commit 2026-04-24 03:18:54 +00:00
feat(federation): outbound mTLS FederationClient (FED-M3-08)
Some checks failed
ci/woodpecker/push/ci Pipeline failed
ci/woodpecker/pr/ci Pipeline failed
21650fb194
Implements FederationClientService — a NestJS injectable that dials peer
gateways over mTLS (undici Agent with cert+sealed-key from federation_peers),
invokes list/get/capabilities verbs, validates responses via Zod, and surfaces
all failure modes as typed FederationClientError with a coherent error code
taxonomy (PEER_NOT_FOUND, PEER_INACTIVE, PEER_MISCONFIGURED, NETWORK,
FORBIDDEN, HTTP_{status}, INVALID_RESPONSE).

Per-peer Agent instances are cached in a Map for the service lifetime;
flushPeer(peerId) invalidates the cache for M5/M6 cert rotation and
revocation events.

Wired into FederationModule providers + exports so QuerySourceService
(M3-09) can inject it.

13 unit tests covering all required scenarios via undici MockAgent +
real sealClientKey/unsealClientKey round-trip.

Closes #462

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
jason.woltje added 1 commit 2026-04-24 03:32:39 +00:00
fix(federation/client): pin Step-CA root, fix lockfile, harden cache test
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
a4a6769a6d
CRIT-1: regenerate pnpm-lock.yaml so apps/gateway resolves undici@7.24.6
(prior PR pushed package.json without lockfile update; CI failed with
ERR_PNPM_OUTDATED_LOCKFILE). Incidentally cleans 57 lines of stale
peer-dep entries.

CRIT-2: cache-hit test no longer swallows resolveEntry errors. Calls the
private method directly twice and asserts identity equality plus a
single DB select, removing the silent-failure path the prior assertion
allowed.

HIGH-1: mTLS Agent now pins Step-CA root via STEP_CA_ROOT_CERT_PATH.
Without the env var resolveEntry throws PEER_MISCONFIGURED, refusing to
dial peers against the public trust store. PEM is read once and cached
on the service instance.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
jason.woltje added 1 commit 2026-04-24 03:57:52 +00:00
fix(federation/client): serialize cache fills, destroy evicted Agent, cover env-var guard
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
37675ae3f2
- HIGH-A: resolveEntry now uses promise-cache pattern so concurrent
  callers serialize on a single in-flight build, eliminating duplicate
  key material in heap and duplicate DB round-trips
- HIGH-B: flushPeer destroys the evicted undici Agent so stale TLS
  connections close on cert rotation
- MED-C: add regression test for PEER_MISCONFIGURED when
  STEP_CA_ROOT_CERT_PATH is unset

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
jason.woltje merged commit b67f2c9f08 into main 2026-04-24 04:30:30 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#508