← all branches

feat/int-base

needs attentionviewing older commit
1794b23 · incrementalPR #268reviewed 2026-07-10 21:59 UTC2H · 2M · 5L · 1I
The branch
Purpose
Land every shared seam the 12 device-integration ports depend on — manifests, catalog seeds, ARN-seed registry entries, CDK stacks, and legacy-registry migration scripts.
Goal
12 solar-brand integrations fully onboarded: manifests, seeds, CDK stacks, workers, multi-granularity collection (5m/15m/1h/1d/1M), live SFN validation on preview.
Sub-goals
  • SG-1: 12 manifests + registration
  • SG-2: catalog seed rows
  • SG-3: ARN-seed REGISTRY + CDK stacks
  • SG-4: live SFN validation per brand
  • SG-7: multi-granularity workers
  • SG-8: live multi-gran validation
  • SG-9: union coverage clamp
  • SG-10: consolidation merges (12 per-brand branches)
  • SG-11: merge origin/main (derivation engine #291)
  • SG-12: combined estate validation
  • SG-13: review remediation (R1-R5 enphase, R6/R12 fronius/apsystems)
The changes (whole branch)
What
SG-13 remediation: R1-R5 enphase token-rotation concurrency fixes; R6 Fronius origin-pin pagination; R12 Fronius/APsystems secret-load fault surfacing + vendor message cap + cadence guard; internal:derivation seed row; merged main (derivation engine #291 x SG-9 coexistence).
Why
Loop C review found confirmed correctness and security holes in enphase rotation protocol and fronius pagination/secret-loading paths.
Areas
services/metrics/integrations+25000150infra/cdk/src/stacks/services/metrics+200010packages/integration-manifests+8005domains/metrics+50030domains/cross-domain+30020packages/database+502scripts/metrics+120020
Blast
290 files, +41611/-211 lines; 12 integration service packages, 12 CDK stacks, migration tooling, metrics read path update.
review-remediation multi-gran oauth-concurrency security-origin-pin
CI· no CI checks found for this PRCodeRabbit· .coderabbit.yaml absent

Findings · 9

correctness2

low

releaseLock SDK fault after successful casWritePair leaves lock held until expiry

services/metrics/integrations/enphase/src/lib/token-manager.ts:250

If releaseLock throws a non-ConditionalCheckFailedException after casWritePair succeeds, .catch(()=>{}) swallows it and lock stays held for up to 90s. pollForWinner losers correctly adopt the freshly written pair via rowUsable() within 2s. Documented tradeoff.

info

isInvalidGrantBody: /valid refresh[ _]token/ could match benign verbose error body

services/metrics/integrations/enphase/src/engine/enlighten-client.ts:54

Pattern would match 'client must provide a valid refresh_token along with credentials', classifying a transient bad-request as terminal dead grant. Extremely unlikely given Enphase's narrow vendor surface but stricter anchor (/please enter a valid refresh[ _]token/i) would eliminate residual risk.

security2

high

APsystems loadCredentialFromArn blanket-catch turns SDK faults into non-retryable MissingSecret

services/metrics/integrations/apsystems/src/engine/ema-client.ts:288

The bare catch block returns null for every exception including throttles, network failures, and AccessDeniedException. The handler maps null -> MissingSecret (400, non-retryable). R12 fixed this in Fronius (catch only ResourceNotFoundException -> null; re-throw everything else). The APsystems equivalent was not updated. Fix: catch (e) { if (e instanceof Error && e.name === 'ResourceNotFoundException') return null; throw e; }

low

console.warn logs vendor-supplied host without length cap

services/metrics/integrations/fronius/src/engine/solarweb-client.ts:226

When a pagination link is rejected, the off-origin host is logged via JSON.stringify. JSON encoding prevents newline injection but the value is uncapped. Consider capping to 253 chars (max valid hostname).

conventions1

high

Enphase credentials.ts blanket-catch turns SDK faults into non-retryable MissingSecret

services/metrics/integrations/enphase/src/lib/credentials.ts:42

The catch block returns { ok: false } for ALL GetSecretValueCommand exceptions. The handler maps !loaded.ok -> MissingSecret (400, non-retryable). Fix: check e.name === 'ResourceNotFoundException' -> return null; re-throw all other SDK errors so the outer handler maps them to UpstreamUnavailable (502, retryable).

tests1

low

R6: isTrustedNextLink subdomain allowance never tested or documented

services/metrics/integrations/fronius/src/__tests__/solarweb-client.test.ts:76

The implementation allows *.api.solarweb.com subdomains via endsWith. No test confirms this is intentional. Add a positive subdomain test if intended, or tighten the check to exact host only.

improvement3

medium

canRotate closure captures mutable bindings — inline comparison would be clearer

services/metrics/integrations/enphase/src/handlers/metrics.lambda.ts:208

The canRotate() closure closes over two mutable let bindings (rotationsSpent, tokenSource) mutated inside paced. Inline boolean at call site would be clearer by co-locating mutation and guard.

medium

isTrustedNextLink re-parses BASE_URL on every call

services/metrics/integrations/fronius/src/engine/solarweb-client.ts:150

new URL(BASE_URL).hostname is evaluated on every call. Extract as module-level const BASE_HOST = new URL(BASE_URL).hostname.

low

loadCredentialFromArn code duplication across Fronius, APsystems, Enphase

services/metrics/integrations/apsystems/src/engine/ema-client.ts:275

GetSecretValueCommand + parse + field extract + secretsClient/resetSecretsClientForTests is verbatim duplicated across all three vendors. R12 fix applied only to Fronius because each copy evolved independently. A shared loadSecretJson helper would prevent this class of drift.

History · 11 commits

  1. f7b9554safeincremental0H · 0M · 0L2026-07-13 20:18
  2. 9ca23adneeds attentionincremental0H · 3M · 3L2026-07-13 19:17
  3. 595484fneeds attentionincremental1H · 3M · 7L2026-07-13 04:29
  4. 2ed62f4safeincremental0H · 0M · 0L2026-07-13 04:13
  5. 2812f54needs attentionincremental1H · 3M · 10L2026-07-11 00:19
  6. 1794b23needs attentionincremental2H · 2M · 5L2026-07-10 21:59current
  7. e5674dcneeds attentionincremental3H · 7M · 6L2026-07-09 19:29
  8. ca45a96needs attentionincremental0H · 3M · 3L2026-07-09 18:56
  9. 5d19484safeincremental0H · 0M · 1L2026-07-08 02:04
  10. dd403feneeds attentionincremental12H · 22M · 12L2026-07-07 20:02
  11. e663ae9needs attentionincremental2H · 7M · 7L2026-07-07 19:02