Skip to content

Conversation

@dev-dami
Copy link
Owner

@dev-dami dev-dami commented Jan 25, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Added security audit capability with --audit-output flag to write audit results to JSON file
    • Introduced configurable preflight checks for memory, dependencies, image size, and timeout thresholds via service configuration
    • Added audit support to service execution endpoint with optional security policy file loading
  • Documentation

    • Updated API and preflight documentation to reflect new configurable thresholds and security audit functionality
  • Updates

    • Modified hello-bun service timeout from 5 to 30 seconds

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 25, 2026

📝 Walkthrough

Walkthrough

Introduces configurable preflight checks and security audit features across the ignite framework. Adds --audit-output CLI option to write audit results to JSON, extends service schema with preflight configuration block for memory, dependencies, image, and timeout thresholds, and updates execution flow to load and apply security policies. Modifies preflight analyzers to use configuration-driven thresholds instead of hardcoded values.

Changes

Cohort / File(s) Change Summary
Documentation
docs/api.md, docs/preflight.md
Updated API documentation to reflect new --audit-output option, security audit response field, and POST /services/:name/execute request/response schema changes for audit, skipBuild, and securityAudit fields. Preflight docs now describe configurable memory, dependencies, image, and timeout thresholds via service.yaml instead of hardcoded values.
Example Configuration
examples/hello-bun/service.yaml
Increased timeoutMs from 5000 to 30000.
CLI
packages/cli/src/commands/run.ts, packages/cli/src/index.ts
Added --audit-output flag to write security audit to JSON file. When auditing, loads policy file and passes it to execution; parses audit from output and writes results if output path provided.
Preflight Analyzers
packages/core/src/preflight/analyze-image.ts, packages/core/src/preflight/analyze-memory.ts, packages/core/src/preflight/analyze-timeout.ts, packages/core/src/preflight/preflight.ts
Replaced hardcoded thresholds with configuration-driven values. Image analyzer accepts optional ImagePreflightConfig with warnMb/failMb. Memory analyzer uses baseMemoryMb, perDependencyMb, warnRatio, failRatio from config. Timeout analyzer uses minMs, maxMs, coldStartBufferMs from config. Dependencies use configurable warnCount/infoCount.
Service Configuration & Schema
packages/core/src/service/load-service.ts, packages/shared/src/types.ts
Added preflight validation in service config loading with new validatePreflightSection helper enforcing positive numeric values. Extended ServiceConfig with optional preflight property containing nested memory, dependencies, image, and timeout configuration blocks.
Execution Layer
packages/core/src/execution/execute.ts
Added optional policy?: SecurityPolicy parameter to ExecuteOptions. When audit enabled, loads policy file for service or uses default, converts to Docker security options, and passes to execution.
HTTP Server
packages/http/src/server.ts, packages/http/src/types.ts
Extended /services/:name/execute endpoint to load policy, pass to execution, and include optional securityAudit field in response when audit enabled. Added SecurityAudit type import and property to ServiceExecutionResponse.
Tests
packages/core/src/__tests__/load-service.test.ts
Updated test assertion for hello-bun service timeout expectation from 5000 ms to 30000 ms.

Sequence Diagram

sequenceDiagram
    participant User
    participant CLI
    participant Core as Execution Core
    participant Docker
    participant Policy as Policy System

    User->>CLI: run --audit --audit-output audit.json
    CLI->>Policy: loadPolicyFile(servicePath)
    Policy-->>CLI: SecurityPolicy (or DEFAULT_POLICY)
    CLI->>Core: executeService({input, audit: true, policy})
    Core->>Docker: dockerRun(with securityOptions from policy)
    Docker-->>Core: metrics + output
    Core->>Policy: parseAuditFromOutput(metrics, policy)
    Policy-->>Core: SecurityAudit
    Core-->>CLI: {success, audit: SecurityAudit}
    CLI->>CLI: writeFile(auditOutput, JSON.stringify(audit))
    CLI-->>User: Audit written to audit.json
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Poem

🐰 Thresholds now dance at our command,
Preflight checks, configured and grand!
Audits write to files with care,
Policies guide with flair—
Security checks everywhere! 🔐✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main changes: adding audit export functionality and making preflight configuration configurable throughout the codebase.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@packages/cli/src/commands/run.ts`:
- Around line 62-66: Replace the path construction that uses join(process.cwd(),
options.auditOutput) so absolute auditOutput paths are preserved; update the
code that writes the audit (the block using options.auditOutput, writeFile and
logger.success) to compute the output path with resolve(process.cwd(),
options.auditOutput) (or resolve(options.auditOutput)) from node:path instead of
join, then pass that resolved path to writeFile and logger.success.

In `@packages/core/src/service/load-service.ts`:
- Around line 116-146: The preflight object validation currently checks
individual numeric fields but misses cross-field ordering rules; after the calls
to validatePreflightSection for 'preflight.memory', 'preflight.image', and
'preflight.timeout' in load-service.ts, read the parsed values from
pf['memory'], pf['image'], and pf['timeout'] and add explicit comparisons that
push errors into the errors array when ordering is wrong (e.g., if
pf['memory']?.warnRatio >= pf['memory']?.failRatio then push
"preflight.memory.warnRatio must be less than preflight.memory.failRatio"; if
pf['image']?.warnMb >= pf['image']?.failMb then push "preflight.image.warnMb
must be less than preflight.image.failMb"; if pf['timeout']?.minMs >=
pf['timeout']?.maxMs then push "preflight.timeout.minMs must be less than
preflight.timeout.maxMs"). Ensure you handle missing or non-number values
gracefully (only perform comparison when both sides are numbers).
🧹 Nitpick comments (3)
packages/core/src/execution/execute.ts (1)

78-78: Minor: Redundant conditional check.

The options.audit ? securityOptions : undefined check is redundant since securityOptions is only defined when options.audit is true (lines 51-54). However, this defensive approach is acceptable for clarity.

♻️ Optional simplification
-    security: options.audit ? securityOptions : undefined,
+    security: securityOptions,
packages/core/src/preflight/analyze-memory.ts (1)

55-84: Consider normalizing dependency thresholds to avoid inverted configs.

If warnCount is set below infoCount, warnings can become unreachable or overly aggressive. A small normalization avoids surprising behavior.

♻️ Suggested normalization
-  const warnCount = dependencyConfig?.warnCount ?? 100;
-  const infoCount = dependencyConfig?.infoCount ?? 50;
+  const warnCountRaw = dependencyConfig?.warnCount ?? 100;
+  const infoCountRaw = dependencyConfig?.infoCount ?? 50;
+  const warnCount = Math.max(warnCountRaw, infoCountRaw);
+  const infoCount = Math.min(infoCountRaw, warnCountRaw);
packages/core/src/preflight/analyze-image.ts (1)

17-18: Normalize warn/fail thresholds to avoid inverted configs.

If failMb is set below warnMb, warnings become unreachable and medium-size images fail unexpectedly. Clamping keeps semantics consistent.

♻️ Suggested normalization
-  const warnThresholdMb = config?.warnMb ?? DEFAULT_IMAGE_SIZE_WARN_MB;
-  const failThresholdMb = config?.failMb ?? DEFAULT_IMAGE_SIZE_FAIL_MB;
+  const warnThresholdMb = config?.warnMb ?? DEFAULT_IMAGE_SIZE_WARN_MB;
+  const failThresholdMb = config?.failMb ?? DEFAULT_IMAGE_SIZE_FAIL_MB;
+  const effectiveWarnMb = Math.min(warnThresholdMb, failThresholdMb);
+  const effectiveFailMb = Math.max(warnThresholdMb, failThresholdMb);
...
-  if (sizeMb > failThresholdMb) {
+  if (sizeMb > effectiveFailMb) {
     return {
       name: 'image-size',
       status: 'fail',
-      message: `Image size ${sizeMb}MB exceeds ${failThresholdMb}MB limit`,
+      message: `Image size ${sizeMb}MB exceeds ${effectiveFailMb}MB limit`,
       value: sizeMb,
-      threshold: failThresholdMb,
+      threshold: effectiveFailMb,
     };
   }
...
-  if (sizeMb > warnThresholdMb) {
+  if (sizeMb > effectiveWarnMb) {
     return {
       name: 'image-size',
       status: 'warn',
-      message: `Image size ${sizeMb}MB exceeds recommended ${warnThresholdMb}MB`,
+      message: `Image size ${sizeMb}MB exceeds recommended ${effectiveWarnMb}MB`,
       value: sizeMb,
-      threshold: warnThresholdMb,
+      threshold: effectiveWarnMb,
     };
   }
...
-  threshold: warnThresholdMb,
+  threshold: effectiveWarnMb,

Also applies to: 30-55

Comment on lines +62 to +66
if (options.auditOutput && audit) {
const outputPath = join(process.cwd(), options.auditOutput);
await writeFile(outputPath, JSON.stringify(audit, null, 2));
logger.success(`Audit saved to ${outputPath}`);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential issue with absolute paths in auditOutput.

Using join(process.cwd(), options.auditOutput) will produce incorrect results if auditOutput is already an absolute path (e.g., /tmp/audit.json becomes /current/dir/tmp/audit.json).

Suggested fix using resolve()
-      const outputPath = join(process.cwd(), options.auditOutput);
+      const outputPath = resolve(options.auditOutput);

resolve() correctly handles both relative and absolute paths - relative paths are resolved against cwd(), while absolute paths are returned as-is. Note: resolve is already imported from node:path via line 2.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (options.auditOutput && audit) {
const outputPath = join(process.cwd(), options.auditOutput);
await writeFile(outputPath, JSON.stringify(audit, null, 2));
logger.success(`Audit saved to ${outputPath}`);
}
if (options.auditOutput && audit) {
const outputPath = resolve(options.auditOutput);
await writeFile(outputPath, JSON.stringify(audit, null, 2));
logger.success(`Audit saved to ${outputPath}`);
}
🤖 Prompt for AI Agents
In `@packages/cli/src/commands/run.ts` around lines 62 - 66, Replace the path
construction that uses join(process.cwd(), options.auditOutput) so absolute
auditOutput paths are preserved; update the code that writes the audit (the
block using options.auditOutput, writeFile and logger.success) to compute the
output path with resolve(process.cwd(), options.auditOutput) (or
resolve(options.auditOutput)) from node:path instead of join, then pass that
resolved path to writeFile and logger.success.

Comment on lines +116 to +146
const preflight = c['preflight'];
if (preflight !== undefined) {
if (typeof preflight !== 'object' || preflight === null) {
errors.push('preflight must be an object');
} else {
const pf = preflight as Record<string, unknown>;

validatePreflightSection(pf['memory'], 'preflight.memory', errors, {
baseMb: 'positive',
perDependencyMb: 'positive',
warnRatio: 'positive',
failRatio: 'positive',
});

validatePreflightSection(pf['dependencies'], 'preflight.dependencies', errors, {
warnCount: 'positive',
infoCount: 'positive',
});

validatePreflightSection(pf['image'], 'preflight.image', errors, {
warnMb: 'positive',
failMb: 'positive',
});

validatePreflightSection(pf['timeout'], 'preflight.timeout', errors, {
minMs: 'positive',
maxMs: 'positive',
coldStartBufferMs: 'positive',
});
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing cross-field validation for threshold ordering.

The validation ensures each field is a positive number, but doesn't validate logical relationships between related thresholds:

  • preflight.memory.warnRatio should be less than failRatio
  • preflight.image.warnMb should be less than failMb
  • preflight.timeout.minMs should be less than maxMs

Invalid configurations (e.g., warnMb: 200, failMb: 100) would pass validation but produce confusing runtime behavior where warnings trigger after failures.

Suggested cross-field validation
       validatePreflightSection(pf['image'], 'preflight.image', errors, {
         warnMb: 'positive',
         failMb: 'positive',
       });
+      
+      // Cross-field validations
+      const memory = pf['memory'] as Record<string, unknown> | undefined;
+      if (memory?.['warnRatio'] !== undefined && memory?.['failRatio'] !== undefined) {
+        if ((memory['warnRatio'] as number) >= (memory['failRatio'] as number)) {
+          errors.push('preflight.memory.warnRatio must be less than failRatio');
+        }
+      }
+      const image = pf['image'] as Record<string, unknown> | undefined;
+      if (image?.['warnMb'] !== undefined && image?.['failMb'] !== undefined) {
+        if ((image['warnMb'] as number) >= (image['failMb'] as number)) {
+          errors.push('preflight.image.warnMb must be less than failMb');
+        }
+      }
+      const timeout = pf['timeout'] as Record<string, unknown> | undefined;
+      if (timeout?.['minMs'] !== undefined && timeout?.['maxMs'] !== undefined) {
+        if ((timeout['minMs'] as number) >= (timeout['maxMs'] as number)) {
+          errors.push('preflight.timeout.minMs must be less than maxMs');
+        }
+      }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const preflight = c['preflight'];
if (preflight !== undefined) {
if (typeof preflight !== 'object' || preflight === null) {
errors.push('preflight must be an object');
} else {
const pf = preflight as Record<string, unknown>;
validatePreflightSection(pf['memory'], 'preflight.memory', errors, {
baseMb: 'positive',
perDependencyMb: 'positive',
warnRatio: 'positive',
failRatio: 'positive',
});
validatePreflightSection(pf['dependencies'], 'preflight.dependencies', errors, {
warnCount: 'positive',
infoCount: 'positive',
});
validatePreflightSection(pf['image'], 'preflight.image', errors, {
warnMb: 'positive',
failMb: 'positive',
});
validatePreflightSection(pf['timeout'], 'preflight.timeout', errors, {
minMs: 'positive',
maxMs: 'positive',
coldStartBufferMs: 'positive',
});
}
}
const preflight = c['preflight'];
if (preflight !== undefined) {
if (typeof preflight !== 'object' || preflight === null) {
errors.push('preflight must be an object');
} else {
const pf = preflight as Record<string, unknown>;
validatePreflightSection(pf['memory'], 'preflight.memory', errors, {
baseMb: 'positive',
perDependencyMb: 'positive',
warnRatio: 'positive',
failRatio: 'positive',
});
validatePreflightSection(pf['dependencies'], 'preflight.dependencies', errors, {
warnCount: 'positive',
infoCount: 'positive',
});
validatePreflightSection(pf['image'], 'preflight.image', errors, {
warnMb: 'positive',
failMb: 'positive',
});
validatePreflightSection(pf['timeout'], 'preflight.timeout', errors, {
minMs: 'positive',
maxMs: 'positive',
coldStartBufferMs: 'positive',
});
// Cross-field validations
const memory = pf['memory'] as Record<string, unknown> | undefined;
if (memory?.['warnRatio'] !== undefined && memory?.['failRatio'] !== undefined) {
if ((memory['warnRatio'] as number) >= (memory['failRatio'] as number)) {
errors.push('preflight.memory.warnRatio must be less than failRatio');
}
}
const image = pf['image'] as Record<string, unknown> | undefined;
if (image?.['warnMb'] !== undefined && image?.['failMb'] !== undefined) {
if ((image['warnMb'] as number) >= (image['failMb'] as number)) {
errors.push('preflight.image.warnMb must be less than failMb');
}
}
const timeout = pf['timeout'] as Record<string, unknown> | undefined;
if (timeout?.['minMs'] !== undefined && timeout?.['maxMs'] !== undefined) {
if ((timeout['minMs'] as number) >= (timeout['maxMs'] as number)) {
errors.push('preflight.timeout.minMs must be less than maxMs');
}
}
}
}
🤖 Prompt for AI Agents
In `@packages/core/src/service/load-service.ts` around lines 116 - 146, The
preflight object validation currently checks individual numeric fields but
misses cross-field ordering rules; after the calls to validatePreflightSection
for 'preflight.memory', 'preflight.image', and 'preflight.timeout' in
load-service.ts, read the parsed values from pf['memory'], pf['image'], and
pf['timeout'] and add explicit comparisons that push errors into the errors
array when ordering is wrong (e.g., if pf['memory']?.warnRatio >=
pf['memory']?.failRatio then push "preflight.memory.warnRatio must be less than
preflight.memory.failRatio"; if pf['image']?.warnMb >= pf['image']?.failMb then
push "preflight.image.warnMb must be less than preflight.image.failMb"; if
pf['timeout']?.minMs >= pf['timeout']?.maxMs then push "preflight.timeout.minMs
must be less than preflight.timeout.maxMs"). Ensure you handle missing or
non-number values gracefully (only perform comparison when both sides are
numbers).

@dev-dami dev-dami merged commit 672664a into master Jan 25, 2026
5 checks passed
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.

2 participants