A deterministic security/quality gate chain for AI-generated code. Point it at a directory; it tells you, with zero LLM in the verdict path, whether that code is safe to ship.
Built for VibeHard's own build pipeline — where AI-generated code is gated before every deploy — and extracted here because the problem ("is this code an AI just wrote actually safe?") isn't specific to any one app generator.
| Gate | What it catches |
|---|---|
sast |
Static analysis (via semgrep) on authored source — injection, unsafe eval, etc. |
secrets |
Committed secrets/credentials (via gitleaks) |
depvuln |
Known CVEs in declared dependencies (via trivy) |
rls |
Missing Row-Level Security on multi-tenant database tables |
migrate |
Dangerous or malformed SQL migrations |
verify |
Clean-room install + build + boot check — does the app actually start? |
completeness |
Does the generated app cover what the spec asked for? (LLM-optional) |
compliance, pii, prod-readiness, proptest, rls-enforce |
Spec-driven checks — no-ops unless a .vibehard/spec.json-shaped spec is present |
Every gate fails closed: if a scanner can't run (missing binary, crash, timeout), that's a
block, never a silent pass. A gate whose precondition doesn't apply reports n/a, not pass —
so an all-n/a board is correctly not a deploy-ready verdict (nothing was actually verified).
bun add @vibehard/gate-check
# or, for the standalone CLI:
bunx @vibehard/gate-check gate ./my-appgate-check gate ./my-app # run every gate, print a report, exit 0/1
gate-check deploy ./my-app # same, plus stamp a signed sentinel file on a full pass
gate-check ./my-app # "gate" is the default subcommandRequires semgrep, gitleaks, and trivy on PATH for the sast/secrets/depvuln gates
respectively — see VibeHard's own Dockerfile for the
exact pinned versions this package's gates were built against. A gate whose binary is missing
fails closed (blocks), it doesn't silently skip.
import { runGate, deployGate, GATES, printReport } from "@vibehard/gate-check";
// Run the full chain, get a structured verdict:
const result = await runGate("./my-app");
console.log(result.passed, result.verdicts);
// Or gate a deploy — writes a signed sentinel file iff every gate passes:
const outcome = await deployGate("./my-app");
if (outcome.sentinel) {
// safe to ship
}
// Print the same report the CLI does:
printReport(result);import { runGate, sastGate, secretsGate, depvulnGate } from "@vibehard/gate-check";
const result = await runGate("./my-app", [sastGate, secretsGate, depvulnGate]);Two gates have optional seams this package doesn't default:
verify— the clean-room build+boot check runs against local Docker/npm by default. Pass aHostProvider(createVerifyGate) to run it in a real sandbox (Fly, E2B, etc.) instead.completeness— reports a blocking "not configured" finding when a spec with real features is present, unless you pass aFunctionalReviewer(createCompletenessGate, or the bundledllmFunctionalReviewer) to actually judge output against the spec.
import { createVerifyGate, createCompletenessGate, llmFunctionalReviewer } from "@vibehard/gate-check";
const verifyGate = createVerifyGate({ runSandbox: myFlySandboxRunner });
const completenessGate = createCompletenessGate({ reviewer: llmFunctionalReviewer({ config: myLlmConfig }) });| Variable | Purpose |
|---|---|
VIBEHARD_SENTINEL_SECRET |
HMAC key signing deployGate's sentinel file. Unset falls back to a random per-process key (fine for CLI/dev/test; set it for cross-process sentinel verification). |
VIBEHARD_HOST_LOCK_DIR |
Overrides the lock directory sast/secrets/depvuln use to serialize concurrent scans on one host. Defaults to /root/.vibehard/.host-lock — override this if your process doesn't run as root. |
bun install
bun test # unit tests (fast, no external tools needed for most)
bun test:integration # also exercises real semgrep/gitleaks/trivy/docker where installed
bun run typecheckMIT