feat/int-base
needs attentionviewing older commit1794b23 · incrementalPR #268reviewed 2026-07-10 21:59 UTC2H · 2M · 5L · 1I- 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)
- 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+25000−150infra/cdk/src/stacks/services/metrics+2000−10packages/integration-manifests+800−5domains/metrics+500−30domains/cross-domain+300−20packages/database+50−2scripts/metrics+1200−20
- Blast
- 290 files, +41611/-211 lines; 12 integration service packages, 12 CDK stacks, migration tooling, metrics read path update.
Findings · 9
correctness2
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.
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
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; }
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
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
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
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.
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.
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
- f7b9554safeincremental0H · 0M · 0L2026-07-13 20:18
- 9ca23adneeds attentionincremental0H · 3M · 3L2026-07-13 19:17
- 595484fneeds attentionincremental1H · 3M · 7L2026-07-13 04:29
- 2ed62f4safeincremental0H · 0M · 0L2026-07-13 04:13
- 2812f54needs attentionincremental1H · 3M · 10L2026-07-11 00:19
- 1794b23needs attentionincremental2H · 2M · 5L2026-07-10 21:59current
- e5674dcneeds attentionincremental3H · 7M · 6L2026-07-09 19:29
- ca45a96needs attentionincremental0H · 3M · 3L2026-07-09 18:56
- 5d19484safeincremental0H · 0M · 1L2026-07-08 02:04
- dd403feneeds attentionincremental12H · 22M · 12L2026-07-07 20:02
- e663ae9needs attentionincremental2H · 7M · 7L2026-07-07 19:02