BLA
BLA (Blind LLM Agents) is the open-source Blue Defense MCP and CLI built to protect software artifacts against automated AI reverse-engineering workflows.
Veylqorix independently verifies AI-generated and human-written code before it is committed, pushed, merged, or deployed — real scanners, one finding schema, a deterministic gate, auditable evidence.
SecurityTypeScript · RustGitHub @soumyachk101OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Scope. Veylqorix reports what its scanners found. It does not certify the absence of vulnerabilities or secrets.
AI coding agents now write a large share of production code — fast, plausible, and occasionally unsafe. Lint and CI pipelines were not designed to verify that volume. Veylqorix fills the gap with four guarantees:
SKIPPED or ERROR with a reason — never a clean pass.crates/veylqorix-engine) is the authoritative secret
scanner, policy engine, and gate; the TypeScript core calls it through a
stdio adapter and degrades to a byte-compatible TypeScript fallback only
when the binary is unavailable (labeled, never silent). See
Rust security engine.0 PASS · 1 FAIL · 2
INCOMPLETE — on every surface, so CI branching is predictable and
auditable.The architecture separates four layers:
veylqorix CLI, the veylqorix-mcp
server, the REST API, a reusable GitHub Actions gate, and portable agent
skills.packages/core) — project detection, scanner adapters,
path confinement, finding normalization, policy evaluation, the gate, and
report rendering (JSON, Markdown, SARIF).A second, standalone engine — the Rust security engine
(crates/veylqorix-engine) — implements the same gate semantics as a
JSON-over-stdio subprocess, byte-compatible with the TypeScript core. It is
covered in depth in Rust security engine.
System architecture — the PNG is the canonical record; diagram sources are not included in the repo (PNG-only is a permanent constraint). Palette and rendering notes are in Veylqorix_Diagrams/README.md.
For the full module-level architecture walkthrough, see Veylqorix_Master_Doc.md.
Scanner adapters reuse existing tools rather than reimplementing them:
| Scanner | Backed by | Offline? |
|---|---|---|
| dependencies | npm audit (Node; lockfile-only when a lockfile is present) / pip-audit (Python, against requirements.txt); cargo audit in the Rust engine for Rust projects (enabled by the adapter via runCargoAudit) | optional |
| secrets | built-in offline rule set (9 named rules, values redacted); optional Gitleaks delegation in the Rust engine — opt-in via the preferGitleaks protocol option and the gitleaks binary on PATH; no CLI flag or MCP parameter turns it on yet | yes |
| licenses | manifest-level license checks (package.json, pyproject.toml) | yes |
| quality | tsc, eslint, ruff, project test scripts — each enabled only when its config exists | no |
--scannersaccepts the three registry IDsdependencies,secrets, andlicenses. Thequalityrow above is the check suite (typecheck, lint, tests), not a scanner ID — it is toggled with--no-tests, and passingqualityto--scannersselects no scanners.
Every scan follows the same evidence-based loop:
Scan lifecycle — Veylqorix_Diagrams/README.md.
INCOMPLETE (exit 2), never to a pass.Prerequisites: Node 22.13+ (CI runs 24), pnpm (corepack enable;
the pinned version in package.json is 11.9.0), and a stable Rust
toolchain (to build the engine). The Node floor comes from pnpm 11.9.0,
which uses node:sqlite and crashes on Node 20 (see the comment in
.github/workflows/ci.yml and in
.github/workflows/veylqorix-gate.yml).
pnpm install
pnpm build # build all packages
pnpm test # vitest unit + integration suite
@veylqorix/[email protected] and @veylqorix/[email protected] are published to npm
(each with a provenance attestation). Install the CLI from the registry:
npm install -g @veylqorix/cli # then: veylqorix --help
Or run it from the monorepo build output:
node apps/cli/dist/index.js scan --root /path/to/repo --json
Exit codes: 0 PASS · 1 FAIL (blocking findings) · 2 INCOMPLETE
(coverage degraded) · 64 usage error · 65 bad root · 70 unexpected
error.
pnpm --filter @veylqorix/api start # API on http://localhost:4173
pnpm --filter @veylqorix/web dev # Next.js dashboard on http://localhost:3000
Open http://localhost:3000/dashboard — full instructions in
docs/dashboard.md. The dashboard proxies /api/* to
the API via Next.js rewrites (NEXT_PUBLIC_API_BASE, default
http://localhost:4173 — set it for cross-origin deployments). When the
API is unreachable, the dashboard shows a live-status banner and offers
clearly labeled sample data (SAMPLE DATA badge, never presented as
live).
All cargo commands target the workspace manifest at crates/Cargo.toml
(there is no Cargo.toml at the repo root):
cargo build --release --manifest-path crates/Cargo.toml --workspace
cargo test --manifest-path crates/Cargo.toml --workspace --all-features
Verify the binary:
crates/target/release/veylqorix-engine --version
# veylqorix-engine 0.1.0 (protocol 1, schema 1)
# requests are one JSON document on stdin, one JSON document on stdout
echo '{"protocolVersion":1,"command":"ping","args":{}}' \
| crates/target/release/veylqorix-engine
| Surface | Where | Status (verified) | Docs |
|---|---|---|---|
CLI (veylqorix) | apps/cli | Real scans, deterministic exit codes, safe dry-run hook installer. @veylqorix/[email protected] is published to npm (with a provenance attestation); install with npm install -g @veylqorix/[email protected]. | docs/cli.md |
MCP server (veylqorix-mcp) | packages/mcp-server | Six Zod-validated tools, root confinement + redaction. Requires pnpm build. | docs/mcp.md |
| REST API + dashboard | packages/api, apps/web | Dependency-free API on :4173; Next.js dashboard on :3000 with a clearly-labeled SAMPLE DATA fallback when the API is down. | docs/api.md · docs/dashboard.md |
| GitHub Actions gate | .github/workflows/veylqorix-gate.yml | Reusable workflow_call gate: real scan + SARIF upload, SHA-pinned actions, least-privilege permissions. Advisory until a branch-protection rule requires the check. | docs/github-actions.md |
| Agent skills | packages/skills | Portable SKILL.md + ready-made MCP configs for Claude Code, Codex, and Cursor. | packages/skills |
Plugin SDK (@veylqorix/plugin) | packages/plugin | Custom scanners/checks that plug into and the gate; shell-free , no silent passes. |
Honest note: the gate and the CLI hooks are advisory until
enforcement (branch protection) is in place. @veylqorix/[email protected] and
@veylqorix/[email protected] are both published to npm (each with a provenance
attestation); the CLI is installable with npm install -g @veylqorix/[email protected]. The registry also still carries a staged
0.0.0-stage placeholder for the CLI (approval is a 2FA-protected owner
step and is optional). See
Honest limitations and
docs/faq.md.
@veylqorix/cli (v0.1.1, MIT, ESM) exposes the veylqorix binary — a thin
adapter over the core engine. Every result comes from a real scanner run.
| Command | What it does |
|---|---|
scan [root] | Full audit + quality gate (default command) |
check [root] | Quality checks only (typecheck, lint, tests); no scanners run, so the gate always reports INCOMPLETE (exit 2) — run scan for a full audit |
report [root] | Human-readable Markdown report |
config validate FILE | Validate a policy JSON file |
hooks install | uninstall | Safe Git hook installer (dry-run by default) |
Options (--format accepts only json | markdown | sarif):
| Option | What it does |
|---|---|
--root PATH | repository to scan |
--staged | scope the secret scan to Git-staged files |
--json / --format json|markdown|sarif | machine-readable or SARIF output |
--report-out FILE | write a Markdown report |
--sarif-out FILE | write a SARIF report |
--offline | skip network-dependent scanners |
--no-tests | skip test/build checks |
--scanners a,b | run only these scanners — IDs: dependencies, secrets, licenses |
--block-on a,b | severities that block the gate |
--policy FILE | load a policy JSON file |
--pre-push | also generate the pre-push hook (hooks install) |
--force, -y / --yes | write hooks (dry-run is the default) |
-h, --help | usage |
hooks install prints the generated .git/hooks/pre-commit (and pre-push
with --pre-push) scripts by default — each with a VEYLQORIX_OVERRIDE=1
escape — and writes only with --yes, backing up existing hooks. Install
errors exit 64. Legacy aliases (audit, deps, secrets, gate,
checks) normalize to scan / check — old scripts keep working.
| Exit | Meaning |
|---|---|
0 | PASS — no blocking findings |
1 | FAIL — one or more blocking findings |
2 | INCOMPLETE — coverage degraded; never treated as a pass |
64 | usage error, invalid policy, or hooks install error |
65 | root or policy file not found, or no staged files under --staged |
70 | unexpected error (catch-all) |
Full reference: docs/cli.md.
@veylqorix/mcp-server exposes the engine to AI coding agents (Claude
Code, Codex, Cursor, and any MCP client) as a stdio server — the
veylqorix-mcp binary at packages/mcp-server/dist/bin.js. Six tools,
exactly:
| Tool | Purpose |
|---|---|
scan_repository | full audit + gate over a confined root |
scan_dependencies | dependency audit (npm audit / pip-audit backends) |
scan_secrets | secret-detection scan (values redacted) |
run_security_checks | type, lint, and test checks |
get_findings | fast findings listing without quality checks; optional limit (int 1–500, default 100) |
validate_fix | re-run the scan to confirm a proposed fix removes the finding |
Tool parameters (root — a non-empty string resolved inside the configured
roots — is required on every tool; everything else is optional):
| Tool | Parameters |
|---|---|
scan_repository | onlyScanners (dependencies / secrets / licenses) · skipQualityChecks (default false) · includeTests (default true) · blockOnSeverity (INFO…CRITICAL, default CRITICAL + HIGH) · blockOnSecrets (default true) |
scan_dependencies | root only |
scan_secrets | root only |
run_security_checks | root · includeTests (default true) |
get_findings | root · onlyScanners · limit (int 1–500, default 100) |
validate_fix | root · scanners (named scanners, not onlyScanners) · findingIds (non-empty strings to verify as resolved) |
Security properties:
root (non-empty string) on
every tool, a scanner enum (dependencies / secrets / licenses),
and a severity enum (INFO…CRITICAL) on scan_repository.blockOnSeverity
(severity filtering is not a general tool parameter); malformed or missing
args surface as isError results, not crashes... traversal, symlink escapes, and absolute paths outside the
roots are rejected (PATH_ERROR) before any filesystem access.AKIA…, GitHub
ghp_/gho_/ghu_/ghs_/ghr_, Slack xox*, Anthropic sk-ant*, generic
sk-, Google AIza*, Stripe-like, emails, PRIVATE KEY blocks) is
applied to text content and recursively over structured content;
matched values are masked as abcd…wxyz [redacted N chars].SKIPPED/ERROR with available: false
— never a pass. Gate results PASS/FAIL/INCOMPLETE map to exit codes
0/1/2 (findings caps: 200 per response for the scan tools,
100 default / 500 max for get_findings; text truncated at 60 000
chars).Configuration is via environment in the server process: VEYLQORIX_ROOTS
(required — comma/whitespace/semicolon-separated roots; without it the
server refuses to start, exit 1, and does not fall back to the current
directory), VEYLQORIX_OFFLINE=1, VEYLQORIX_TIMEOUT_MS (default
120000). Example .mcp.json (run pnpm build first):
{
"mcpServers": {
"veylqorix": {
"command": "node",
"args": ["packages/mcp-server/dist/bin.js"],
"env": {
"VEYLQORIX_ROOTS": "/path/to/your/repo"
}
}
}
}
Setup and client configs: docs/mcp.md.
@veylqorix/api is a dependency-free Node http server on port 4173
(VEYLQORIX_API_PORT to override). By default the API binds to
127.0.0.1 (loopback-only). To expose it on another address you must
run it in protected mode: set VEYLQORIX_API_HOST and
VEYLQORIX_API_TOKEN — a non-loopback host without a token refuses to
start. In protected mode every /api route requires the bearer token
(GET /api/health stays open so orchestrators can probe) and is checked
with a timing-safe comparison. CORS is an exact-origin allowlist from
VEYLQORIX_CORS_ORIGINS — no wildcard, no credentials. Scans are
asynchronous: POST /api/scan creates a RUNNING record, starts the
core audit in the background, and returns 202 with the scan id; clients
poll GET /api/scan/:id on the stable status field until DONE
(findings, gate, coverage, and exit code available), ERROR, or
INTERRUPTED (a scan still running when the API process exited — re-run
it; it is never surfaced as completed). Unknown routes return 404.
| Endpoint | Behavior |
|---|---|
GET /api/health | liveness check |
GET /api/repositories | list registered repositories |
POST /api/repositories | register a repository: { "path": "/abs/path", "name": "optional" } → 201 (name defaults to the path basename; missing path → 400; outside VEYLQORIX_ALLOWED_ROOTS → 403) |
POST /api/scan | start a scan: { "repoId" } or ad-hoc { "path" } (auto-registered) → 202 + scan id |
GET /api/scan/:id | poll scan status |
GET /api/scan/:id/findings | normalized findings for a scan |
GET /api/scan/:id/report?format=markdown|json|sarif | rendered report |
GET /api/scans?repoId= | scan history, newest first |
GET /api/scans/:id | one scan record |
GET /api/reports/:scanId?format=… | report download (Content-Disposition: attachment) |
GET /api/repo/:id/latest | newest finished scan for a repo |
GET /api/repo/:id/history | scan history for a repo |
Configuration (environment):
| Variable | Default | Effect |
|---|---|---|
VEYLQORIX_API_PORT | 4173 | listen port |
VEYLQORIX_API_HOST | — | bind address; non-loopback requires VEYLQORIX_API_TOKEN or the API refuses to start |
VEYLQORIX_API_TOKEN | — | bearer token: every /api route except GET /api/health requires it (timing-safe check) |
VEYLQORIX_CORS_ORIGINS | — | comma-separated exact-origin CORS allowlist (no wildcard, no credentials) |
VEYLQORIX_ALLOWED_ROOTS | — | comma/space-separated root allow-list; paths outside it get 403 on registration or scan (least privilege) |
VEYLQORIX_OFFLINE | off | skip network-dependent scanners (=1) |
VEYLQORIX_INCLUDE_TESTS | off | include test/build checks (=1) |
VEYLQORIX_TIMEOUT_MS | 120000 | per-scanner timeout in ms (applied to each scan) |
VEYLQORIX_MAX_CONCURRENT_SCANS | 2 | maximum concurrent scans; excess requests get 429 with a retry hint |
VEYLQORIX_BODY_LIMIT_BYTES | 1048576 | maximum JSON request body (1 MiB); larger requests get 413 |
VEYLQORIX_MAX_FIELD_LENGTH | 4096 | maximum accepted length of path/name/repoId fields; longer values get 400 |
.env.example documents every variable above with its safe default.
Storage persists to a JSON file: repositories and scans are written to
VEYLQORIX_STORE_PATH (default ./.veylqorix-store.json) with atomic
writes (temp file + fsync + rename), so state survives restarts. Scans
still RUNNING when the process exits are re-stamped INTERRUPTED on
the next start — never surfaced as completed. The store is single-process:
two API processes sharing one file will lose interleaved writes. The
store file holds repository paths + names, scan metadata, and
redacted findings only. Full reference: docs/api.md.
The dashboard (apps/web, Next.js 15 + React 19) shows real scan
data from the API when it is reachable — a LIVE badge — with live
scans polled every 2.5 s. If the API is down or the repository list is
empty, a banner explains it (with the command to start the API) and a
Seed demo data button loads sample data under a gold SAMPLE DATA
badge; demo mode disables report downloads and offers Clear sample
data & refresh. Sample data is never presented as a live result.
The dashboard is also the write surface: register a repository by
absolute path (optional display name) and start a scan directly in the
UI — scans are created via POST /api/scan (ad-hoc paths are
auto-registered) and polled every 2.5 s until DONE; the Scan
button on a selected repo re-scans it. Report downloads are enabled
only for live DONE scans, never in sample mode.
| What you see | Where it comes from |
|---|---|
| Gate result chip | PASS / FAIL / INCOMPLETE + scan status + exit code |
| Findings panel | sorted by severity, with scanner/rule, location, redacted evidence, remediation, advisory, SECRET badges |
| Scanner status grid | which checks actually ran (PASS / FAIL / ERROR per scanner) |
| Scan history | scan id, status, started date |
| Severity + policy rail | severity breakdown and active policy |
| Report downloads | JSON / SARIF / Markdown, enabled only for DONE scans (never in sample mode) |
The monorepo ships veylqorix-gate as a reusable workflow_call GitHub
Actions workflow (.github/workflows/veylqorix-gate.yml) — CI
enforcement that is independent of local hooks.
| Input | Type | Default | Meaning |
|---|---|---|---|
root | string | . | path to scan, relative to the repo root |
run-tests | bool | false | run test/build quality checks |
block-on | string | HIGH,CRITICAL | comma-separated severities that block |
What it does: check out the calling repo (fetch-depth: 0) → prepare the
CLI (build from source when apps/cli is present; otherwise require a
global veylqorix and fail loudly if it is missing) → run one
authoritative scan that feeds every report format from the same audit
result (JSON + Markdown + SARIF) → upload the veylqorix-scan
artifact (veylqorix.json, veylqorix.md, SARIF, stderr) → validate
the SARIF (2.1.0 with at least one run) before uploading it to GitHub
code scanning → evaluate exit codes: 0 PASS · 1 FAIL · 2
INCOMPLETE (never green — an incomplete scan fails the gate).
Permissions are least-privilege (contents: read,
security-events: write), no secrets are forwarded, and every
third-party action is pinned to a full commit SHA.
Minimal calling workflow:
# .github/workflows/veylqorix-gate.yml in the calling repo
name: Veylqorix Gate
on: pull_request
permissions:
contents: read
jobs:
gate:
uses: soumyachk101/Veylqorix/.github/workflows/veylqorix-gate.yml@main
Enforcement is advisory until you make it required. The workflow does not block merges by itself. It becomes authoritative only when the calling repository requires its status check via branch protection or a ruleset. Until then: findings are produced and the check fails — but a merge can still be forced. See docs/github-actions.md.
Veylqorix reaches AI coding agents with one security core and several compatible entry points — no duplicated security rules:
AI integration model — Veylqorix_Diagrams/README.md.
| Surface | What it does |
|---|---|
| MCP server | six validated tools (see MCP server) — the programmatic entry point |
Portable skill (packages/skills) | SKILL.md in Agent Skills format that guides the agent's verify-don't-trust workflow, plus ready-made MCP client configs for Claude Code, Codex, and Cursor |
| Git hooks | hooks install adds a safe pre-commit/pre-push layer: dry-run by default, backs up existing hooks, never touches git config |
@veylqorix/plugin is a thin scanner extension SDK over the core:
author custom scanners and quality checks (wrapping an in-house SAST tool,
an SBOM policy check, a coverage gate, a house rule) and hand that
evidence to runAudit — it flows into the unified finding schema and the
same authoritative gate, never a re-implemented one.
import { definePlugin, runPluginAudit } from '@veylqorix/plugin';
const myPlugin = definePlugin({
id: 'acme/security',
scanners: [
{
id: 'acme-sast',
// the context gives you `root`, `timeoutMs`, and a shell-free `exec`
run: async (ctx) => {
const { code, stdout } = await ctx.exec('semgrep', ['run', '--json'], {
cwd: ctx.root,
timeoutMs: ctx.timeoutMs,
});
return normalizeMyToolOutput(stdout); // Finding[] — normalized for you
},
},
],
checks: [
{
id: 'coverage-floor',
command: 'node scripts/check-coverage.mjs',
run: async (ctx) => runCoverageFloor(ctx),
},
],
});
const result = await runPluginAudit({ root: '/path/to/repo' }, myPlugin);
process.exit(result.exitCode); // 0 · 1 · 2
Security properties:
ERROR, which blocks the gate under the default policy and
degrades coverage to INCOMPLETE (exit 2). A missing optional tool is
reported SKIPPED with a note — never a clean result.normalizeFindings (deterministic ids, severity/confidence clamping,
malformed entries dropped), so reports and gate reasons behave exactly
as if a built-in scanner had produced them.execFileAsync (explicit argument arrays, hard timeouts, bounded
output).The gate stays single-sourced: plugin evidence is merged into the audit
and evaluated by the Rust engine when its binary is available, else by the
byte-compatible TypeScript engine — whichever authority runAudit already
uses. Full reference and authoring guide: docs/plugin.md.
@veylqorix/core is the shared engine the CLI, MCP, and API all call.
Drive a scan directly:
import { runAudit } from '@veylqorix/core';
const result = await runAudit({ root: '/path/to/repo' });
console.log(result.gate.result); // 'PASS' | 'FAIL' | 'INCOMPLETE'
process.exit(result.exitCode); // 0 · 1 · 2
runAudit(options) — root is required; the rest override defaults:
policy (a Partial<Policy> merged over the defaults —
Policy and exceptions) · online (default
true; false marks the network-dependent scanners SKIPPED) ·
scannerTimeoutMs (default 120000) · includeTests (default true) ·
onlyScanners / skipScanners (registry IDs: dependencies,
secrets, licenses) · stagedFiles (secret scan restricted to these
root-relative files) · strict (any blocking reason → exit 1, any
coverage gap → exit 2; also returns softExitCode for the default
mapping) · preferGitleaks · scanId. Returns
AuditResultWithExit: gate (result, blockingReasons,
incompleteReasons, coverage, appliedExceptions), findings,
scanners, checks, exitCode. Subpath exports:
@veylqorix/core/gate (evaluateGate, exitCodeFor),
@veylqorix/core/report (toJson, toMarkdown, toSarif),
@veylqorix/core/scanners/*.
crates/veylqorix-engine is a standalone, pure-Rust security engine
(binary veylqorix-engine, v0.1.0, edition 2021, MIT) that implements
the same gate semantics as the TypeScript core: deterministic policy
evaluation, gate decision, exit-code mapping, and the offline secret
scanner — exposed over a bounded JSON-over-stdio protocol. The cargo
workspace lives at crates/Cargo.toml (resolver 2, single member),
with a release profile of opt-level 3 + thin LTO so the per-scan-spawned
binary stays small and fast. Dependencies are pure Rust (serde,
serde_json, regex, sha1/digest); the version trio is engine
0.1.0, protocol 1, schema 1.
Versions. The Rust engine is 0.1.0 (protocol 1, schema 1); the workspace packages
@veylqorix/coreand@veylqorix/cliare 0.1.1 (other packages are still at 0.1.0 — see Roadmap). The release table: CHANGELOG.md.
Integration status (honest). The engine is wired into the application through the TypeScript adapter
packages/core/src/engine/index.ts, which resolves the binary (VEYLQORIX_ENGINEenv var, thenveylqorix-engineonPATH, then documented install locations, then an upward walk to acrates/target/{release,debug}/veylqorix-enginein the repo) and is invoked byrunAuditinpackages/core/src/audit.ts. CLI, MCP, and API all funnel throughrunAudit, so they use the engine when the binary is present; when it is not,runAuditdegrades to the byte-compatible TypeScript secret scanner and policy engine and records an explicit "degraded compatibility mode" note on the secret scanner result, so a degraded run is never presented as a full engine run. The label is a scanner note, not a completeness gap: a clean repo with the engine binary missing still evaluatesPASS(exit 0, FULL coverage), and coverage degrades toINCOMPLETE(exit 2) only when a scanner separately could not run (offline mode, a missing tool, an errored check) — never a silent pass. The engine runs the secret scan and evaluates the gate over the merged evidence, and its decision drives the exit codes the surfaces report.
One JSON document per direction on stdio (camelCase, deny_unknown_fields
on requests; 8 MiB input cap, 16 MiB output cap). --version prints the
banner; any other positional argument exits 64.
| Command | What it does |
|---|---|
ping | returns engine + protocol versions; no work |
scan | runs the engine's built-in scanners (secrets, optional Gitleaks, cargo audit) plus the gate; root required |
evaluate | gate-only over supplied evidence (findings / scannerResults / checks); no filesystem access |
Request envelope (command + args; most ScanArgs fields have defaults
— root, detection, policy overrides, stagedFiles, strict,
findings, scannerResults, checks, runSecrets, preferGitleaks,
runCargoAudit, timeoutMs defaulting to 120 000 ms, scanId):
{
"protocolVersion": 1,
"command": "scan",
"args": {
"root": ".",
"runSecrets": true,
"runCargoAudit": true,
"strict": true
}
}
The response envelope carries protocolVersion, engineVersion,
schemaVersion, ok, the gate decision, exitCode, an optional
strictExitCode (when strict: true), deterministically sorted
findings, engineScannerResults, and notes.
| Code | Context | Meaning |
|---|---|---|
0 | gate | PASS — no blocking reasons, full coverage |
1 | gate | FAIL — one or more blocking reasons |
2 | gate | INCOMPLETE — no blocking reasons but coverage degraded; never a pass |
64 | protocol | MALFORMED — bad JSON, unknown fields, unsupported protocol version, or an unrecognized argument |
65 | protocol | ROOT_NOT_FOUND — the audit root does not exist or is not a directory |
70 | protocol | INTERNAL — engine internal error |
With strict: true, a strict mapping is also returned: any blocking
reason forces 1, any completeness gap forces 2. A scanner being
unavailable inside a scan never causes 64/65/70 — it is recorded as a
SKIPPED or ERROR scanner result (degraded coverage), so an error can
never become a silent pass.
aws-access-key-id,
github-personal-access-token, github-github-pat-classic,
slack-token, private-key-block, openai-api-key,
anthropic-api-key, google-api-key, heroku-api-key) applied to 18
default extensions (.js .ts .jsx .tsx .mjs .cjs .py .go .rs .java .rb .php .cs .sh .yaml .yml .json .env). Runs fully local — source
code never leaves the machine.****; longer
values keep the first 4 and last 4 characters with a length
annotation, e.g. AKIA…CDEF (20 chars, redacted). Byte-compatible
with the TypeScript redactSecret; golden vectors assert the match.preferGitleaks is set and gitleaks
is on PATH, the engine runs gitleaks detect --no-git --no-banner --no-color -s <root> -f json -r - (JSON report on stdout; exit 0 =
clean, 1 = leaks found, 2 = tool error — flags verified against
gitleaks 8.30.1) under the same timeout; findings are normalized and
re-redacted. Missing binary → SKIPPED (falls back to the offline
rule-set); timeout → ERROR with a degraded-coverage note. Never a
silent pass.cargo audit -f <root>/Cargo.lock --json, run with the audit root as the working directory (the lockfile
is passed via -f). No Cargo.lock → SKIPPED ("not a pass");
missing cargo-audit binary → SKIPPED with an install hint
(cargo install cargo-audit); timeout → ERROR. An unrecognized
output shape (older or changed cargo-audit payload) → ERROR, so an
audit the engine cannot read is never turned into a pass. SKIPPED-never-PASS
semantics are documented in the module header and enforced in code.timed_out), and retain
output as a bounded trailing window (256 KB per stream, with a
[truncated N KB of total] marker).SECRETS-8C5562BA), byte-compatible with the TypeScript core.The suite currently holds 75 tests (41 unit + 34 integration; the count is
cargo test-derived and changes with the code — re-verify when it does):
| Suite | File | What it pins |
|---|---|---|
| Golden | tests/golden.rs | TypeScript golden vectors — finding ids byte-identical to the TS core |
| Gate differential | tests/gate-differential.rs | gate decisions match reference behavior across policy inputs |
| Secret parity | tests/secret-parity.rs | secret detection + redaction parity with the TS scanner |
| Parity fixtures | tests/parity-fixtures.rs | fixture-driven differential + protocol parity with the TS core |
| Protocol e2e | tests/protocol-e2e.rs | spawns the real binary and round-trips the stdio protocol |
| External tools | tests/external-tools.rs | live cargo-audit (vulnerable + clean lockfile) and gitleaks delegation paths, run only when the tools are installed |
CI (.github/workflows/ci.yml) runs against the workspace manifest:
cargo fmt --check, cargo clippy -- -D warnings, cargo test --all-features, and cargo build --release (stable toolchain with
clippy + rustfmt, all third-party actions pinned to commit SHAs) —
before the TypeScript test suite. CI also installs cargo-audit and
pinned gitleaks v8.30.1 (non-fatally, continue-on-error, the asset's
SHA-256 is verified) so the external-tools tests exercise the real
delegation paths when the install succeeds; when a
tool is unavailable the tests log an honest SKIP rather than skipping the
scan silently.
A veylqorix-policy.json (sample at the repo root)
controls what blocks the gate. The shipped default blocks on
CRITICAL/HIGH findings, any secret, test failure, scanner error, or
skipped check; it reports MEDIUM/LOW:
{
"blockOnSeverity": ["CRITICAL", "HIGH"],
"blockOnSecrets": true,
"blockOnTestFailure": true,
"blockOnScannerError": true,
"blockOnSkippedCheck": true,
"reportOnSeverity": ["MEDIUM", "LOW"],
"maxExceptions": 5,
"exceptions": []
}
Exceptions are deliberately unfriendly to gaming:
maxExceptions (5 by default),An AI agent cannot suppress findings or raise thresholds without an entry that is present in the committed policy file and visible in the report.
The CLI auto-discovers a policy in the scan root when --policy is
not passed, in this order: veylqorix-policy.json,
.veylqorix-policy.json, veylqorix.policy.json. An invalid policy
file exits 64; veylqorix config validate FILE reports issues
without scanning (exit 0 valid, 64 invalid, 65 file not found).
An exception entry is an object — ruleId and a non-empty reason
are required; expires (ISO 8601) and pathGlob are optional:
{
"exceptions": [
{
"ruleId": "github-github-pat-classic",
"reason": "Legacy token rotated on 2026-09-01",
"expires": "2026-12-31",
"pathGlob": "docs/**"
}
]
}
Expired or undocumented exceptions are ignored at gate time. Glob
semantics: * stays within a path segment, ** crosses segments.
403
outside).contents: read + security-events: write, forwards no secrets,
and pins every third-party action to a commit SHA.See SECURITY.md for the threat model and responsible disclosure.
Veylqorix reports what its scanners found. It does not certify the absence of vulnerabilities or secrets. Specifically:
pip-audit, gitleaks, cargo-audit), or a missing
lockfile make a scanner SKIPPED or ERROR, which can make the
gate INCOMPLETE (exit 2) — never a pass.@veylqorix/[email protected]
and @veylqorix/[email protected] are both published to npm (each with a
provenance attestation); the CLI is installable with npm install -g @veylqorix/[email protected], so non-monorepo callers can install it directly.runAudit
(packages/core/src/audit.ts) calls it through the adapter in
packages/core/src/engine/index.ts when the veylqorix-engine
binary is available, and degrades to the byte-compatible TypeScript
fallback with an explicit "degraded compatibility mode" note on the
secret scanner result when it is not — never a silent fallback. A
clean repo with the engine binary missing still exits 0 (PASS, FULL
coverage); exit 2 (INCOMPLETE) appears only when a scanner
separately could not run.VEYLQORIX_STORE_PATH, default ./.veylqorix-store.json):
repositories and scan history survive restarts, and in-flight scans
recover as INTERRUPTED, never as completed.Full answers: docs/faq.md · deeper failure explanations: docs/troubleshooting.md.
Progress is tracked in CHANGELOG.md (0.1.1 released 2026-10-27). Open items:
packages/api,
packages/mcp-server, packages/plugin, packages/skills, and
apps/web still read 0.1.0, while the root, packages/core, and
apps/cli are at 0.1.1.@veylqorix/cli to the npm registry:
done 2026-10-29 — @veylqorix/[email protected] and
@veylqorix/[email protected] are both on the registry (each with a
provenance attestation). The CLI trusted-publisher record was
registered, the v0.1.1 tag re-pointed to the commit carrying
the fixed release workflow, and release run 40932201210
published the CLI (core was never republished).Veylqorix_Diagrams/
PNGs are the canonical record and their sources are not included in
the repo; PNG-only is a permanent constraint, so no SVG, Mermaid,
PDF, or new diagram-generation pipeline will be addedThe build plan that shipped 0.1.x is archived here:
Eight-day build roadmap (archived) — Veylqorix_Diagrams/README.md.
pnpm install
pnpm build # build all packages
pnpm typecheck # strict TypeScript, zero errors
pnpm lint # ESLint, zero warnings
pnpm test # vitest unit + integration suite
Engine quality gates (all against the workspace manifest at
crates/Cargo.toml):
cargo fmt --manifest-path crates/Cargo.toml --all -- --check
cargo clippy --manifest-path crates/Cargo.toml --workspace --all-targets --all-features -- -D warnings
cargo test --manifest-path crates/Cargo.toml --workspace --all-features
The TypeScript suite covers the gate logic, finding normalization,
secret redaction, path confinement, and policy validation against the
fixture repositories in tests/fixtures/ (including clean-repo,
failing-tests-repo, insecure-repo, no-scanner-repo, secret-repo,
vulnerable-repo — fake data only, never real credentials), the full
audit flow, the REST API over real HTTP, and the MCP server through an
in-memory client. The Rust suite adds 75 tests (see
Rust security engine).
Ground rules (full text in CONTRIBUTING.md): no
fabricated results; never weaken the gate to make a test pass
(INCOMPLETE exit 2 is never a pass); redact secrets everywhere; no
new dependencies without justification. Pull requests use the
template, whose verification block
requires pnpm build && pnpm typecheck && pnpm lint && pnpm test.
Known caveats (fix-forward, not blockers): apps/cli/src/index.ts
still duplicates the shared apps/cli/src/cli-args.ts parser (the
runtime behavior documented in CLI follows index.ts).
Two suites, both run in CI:
pnpm test (vitest): the unit and integration
suites in tests/ plus the package-local tests under
packages/*/src and apps/*/src, against the fixture repositories
in tests/fixtures/ (fake data only).cargo test --manifest-path crates/Cargo.toml --workspace --all-features: 75 tests covering the gate, policy,
redaction, safe exec, and the stdio protocol. See
Rust security engine — Tests and CI.A full PR check is pnpm build && pnpm typecheck && pnpm lint && pnpm test (required by the pull request template), plus the three
engine quality gates in Development.
packages/core engine: detection, scanners, policy, gate, reports,
path safety, git + hook helpers, policy validation
packages/mcp-server MCP server (six tools) for AI coding agents
packages/api dependency-free REST API that runs real scans
packages/skills portable AI-agent skill + per-client MCP configs
packages/plugin scanner extension SDK (custom scanners + checks)
apps/cli the veylqorix command-line interface
apps/web Next.js dashboard (real API data, labeled demo fallback)
crates/ Rust security engine (cargo workspace, veylqorix-engine)
docs/ per-surface documentation
tests/ vitest suite + fixture repositories (fake data only)
Veylqorix_Diagrams/ rendered architecture diagrams + palette notes
Veylqorix_Master_Doc.md full engineering master doc (architecture, lifecycle, engine, publishing)
The core engine is independent of every front-end. CLI, MCP, and API
are thin adapters that all call the same runAudit and policy/gate
functions, so there is exactly one place where security logic lives —
backed by the Rust engine when its binary is available, and by the
byte-compatible TypeScript fallback (explicitly labeled as degraded)
when it is not.
Reading the whole thing first? Start here — it's the single source of truth for how every part of the system fits together.
📘 Open the Master Technical Document →
Architecture · end-to-end scan lifecycle · repository map · data model · gate semantics · all surfaces — cross-referenced and current with the code.
| Doc | What it covers |
|---|---|
| docs/cli.md | commands, options, exit codes, hooks install |
| docs/mcp.md | MCP setup + example .mcp.json |
| docs/api.md | REST API reference |
| docs/dashboard.md | running the API + dashboard, environment variables |
| docs/github-actions.md | reusable gate workflow, enforcement via branch protection |
| docs/plugin.md | plugin SDK: authoring custom scanners/checks, API reference, security properties |
| docs/engine.md | the Rust security engine: crate layout, stdio protocol, security properties, build/test |
| docs/faq.md | honest limitations and general questions |
| docs/troubleshooting.md | "why did it fail" answers |
| Veylqorix_Diagrams/README.md | diagram palette and rendered PNGs |
| Veylqorix_Master_Doc.md |
pnpm test tests/unit/gate.test.ts, add a scanner or check in
packages/core/src/scanners/, and use the Plugin SDK
(docs/plugin.md) instead of reimplementing the gate.good first issueMIT — Copyright (c) 2026 Veylqorix. See LICENSE.
runAuditexecworkspace:* until publish.| docs/plugin.md |
| Rust security engine | crates/veylqorix-engine | Binary + JSON-stdio protocol + 75 tests; called by the TypeScript core through the engine adapter. | below |
maxExceptions = 5, and applied exceptions are
recorded in the gate decision.| full engineering master doc — read this first |