@runenv/agent-edge
v0.3.0
Published
Local exposure scanner and secure runtime edge for RunEnv Agent Guard
Readme
@runenv/agent-edge
Find agent-readable credentials and unsafe agent configuration locally, without uploading credential values.
@runenv/agent-edge is the open RunEnv Agent Guard edge scanner. It provides a bounded command-line doctor and an ESM API for producing local exposure reports and explicit privacy-preserving upload projections.
Installation
npm install @runenv/agent-edgeCurrent package version: @runenv/[email protected]. See CHANGELOG.md for release notes.
Requires Node.js 20 or newer.
Quick Start
Scan the current directory and render a human-readable report:
npx @runenv/agent-edge doctor .Request the same local report as JSON:
npx @runenv/agent-edge doctor . --jsonThe doctor command never publishes a report. Its exit codes are:
| Code | Meaning |
| --- | --- |
| 0 | The scan completed without critical or high-severity findings. |
| 1 | The command or scan failed. |
| 2 | The scan found at least one critical or high-severity exposure. |
Treat local JSON as sensitive metadata: it contains relative paths and may contain keyed credential fingerprints when the programmatic API enables them. It never contains the detected credential values.
What It Detects
The scanner currently checks for:
- literal credential-like assignments in
.env*files; - high-confidence API keys, access tokens, private keys, and credential-bearing service URLs;
- credentials embedded in MCP and supported agent configuration files;
- enabled auto-approval or permission-bypass settings;
- wildcard tool allowlists;
- well-known cloud and package credential files inside the scan root;
- overly broad permissions on credential-bearing files on non-Windows systems; and
- duplicate credentials when a caller supplies a
fingerprintKey.
Generated findings include remediation guidance, but never the matched credential value.
Programmatic API
import { randomBytes } from 'node:crypto'
import {
renderExposureReport,
scanExposure,
toExposureUpload,
} from '@runenv/agent-edge'
const report = scanExposure(process.cwd(), {
fingerprintKey: randomBytes(32),
limits: {
maxEntries: 10_000,
maxFiles: 2_000,
},
})
console.log(renderExposureReport(report))
// This creates a privacy-preserving DTO; it does not send a network request.
const upload = toExposureUpload(report, randomBytes(32))Exports
| Export | Purpose |
| --- | --- |
| scanExposure(root, options?) | Synchronously scan a directory and return a validated ExposureReport. |
| renderExposureReport(report) | Render a validated report for a terminal or log stream. |
| toExposureUpload(report, uploadKey) | Create a validated upload-safe projection using a key of at least 32 bytes. |
| DEFAULT_EXPOSURE_SCAN_LIMITS | Read-only default traversal and resource budgets. |
| AGENT_EDGE_VERSION | Scanner version recorded in generated reports. |
| ExposureScanLimits | TypeScript interface for all scanner budgets. |
| ExposureScanOptions | TypeScript interface for limits, clock injection, and keyed fingerprints. |
scanExposure() resolves the requested root to a real directory and throws when the root is invalid or a supplied limit is not a positive integer. The scanner uses synchronous filesystem operations, so server applications should run large scans outside latency-sensitive request handlers.
Scan Limits
Every scan is bounded. The defaults are:
| Limit | Default |
| --- | ---: |
| Visited directory entries | 50,000 |
| Scanned files | 5,000 |
| Bytes per file | 1,000,000 |
| Total scanned bytes | 50,000,000 |
| Findings | 500 |
| Directory depth | 20 |
| Duration | 30,000 ms |
The entry budget is charged before ignore, file-type, extension, symlink, and readability filters. Symlinks are not followed, and common generated or dependency directories such as .git, node_modules, dist, and vendor are skipped. A report is marked truncated when a terminal budget is reached; inspect its skipped entries before treating it as complete.
Privacy and Security
Local scanning requires no RunEnv account and performs no network request.
scanExposure() deliberately retains relative paths so a developer can remediate local findings. If fingerprintKey contains at least 32 bytes, it also emits HMAC fingerprints used to identify duplicate credentials within the scan. Keep that local report on the device.
Before any publication, call toExposureUpload() with a separate key of at least 32 bytes. The projection:
- replaces relative paths with keyed path hashes;
- removes paths from skipped-file entries;
- removes credential fingerprints;
- replaces stable local finding IDs with report-scoped keyed digests; and
- revalidates the result against the strict exposure upload contract.
The function only returns a DTO. Authentication, authorization, transport, and the decision to publish remain the caller's responsibility.
Compatibility
- Node.js
>=20.0.0 - ECMAScript modules (
import/ ESM) @runenv/[email protected]- macOS, Linux, and Windows filesystem paths; file-mode findings are intentionally omitted on Windows
