@boldblackai/harness 1.9.0 → 1.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -2
- package/bin/harness.js +16 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -174,7 +174,9 @@ npx @boldblackai/harness -a hermes -e .env -m anthropic/claude-sonnet-4-5 -p "ad
|
|
|
174
174
|
npx @boldblackai/harness -a hermes -e .env -m openrouter/auto -p "add tests"
|
|
175
175
|
```
|
|
176
176
|
|
|
177
|
-
Common keys: `ANTHROPIC_API_KEY`, `OPENROUTER_API_KEY`, `OPENAI_API_KEY`, `GOOGLE_API_KEY`, [and others](https://github.com/NousResearch/hermes-agent/blob/main/.env.example). LM Studio context length should be at least 64k tokens.
|
|
177
|
+
Common keys: `ANTHROPIC_API_KEY`, `OPENROUTER_API_KEY`, `OPENAI_API_KEY`, `GOOGLE_API_KEY`, `GLM_API_KEY` (Z.AI), [and others](https://github.com/NousResearch/hermes-agent/blob/main/.env.example). LM Studio context length should be at least 64k tokens.
|
|
178
|
+
|
|
179
|
+
**Multiple API keys in one env file.** With `--env-file`, each agent auto-detects a provider from the keys present. If the file contains keys for more than one provider (e.g. `OPENROUTER_API_KEY` and `GLM_API_KEY`), the agent may not pick the one you expect — `pi` and `opencode` follow their own detection order. For one-shot runs, use a single-provider env file, or select the agent and model explicitly (e.g. `harness -a hermes -e .env -m zai/glm-4.7 -p "..."`).
|
|
178
180
|
|
|
179
181
|
## Security model
|
|
180
182
|
|
|
@@ -189,6 +191,8 @@ Each run starts the container with:
|
|
|
189
191
|
- `--security-opt seccomp=...` — inline seccomp profile blocks `socket(AF_ALG)` to prevent kernel crypto API access (a known container escape vector)
|
|
190
192
|
- Only your mounted directory (or single file with `-f`) is visible to the agent
|
|
191
193
|
|
|
194
|
+
Running harness from your home directory is blocked by default, since that would mount all of `$HOME` into the container — exposing dotfiles, credentials, and SSH keys to the agent and giving it a noisy, unfocused workspace. If the resolved working directory equals your home, harness exits with an error. Pass `--mount-entire-home` to opt in for the rare case where mounting all of `$HOME` is genuinely intended. `--file` mode is unaffected (it mounts a single file, not the working directory).
|
|
195
|
+
|
|
192
196
|
These hardening flags are docker-specific. Under `HARNESS_CONTAINER_RUNTIME=apple`, `--security-opt` is not applied (apple/container workloads are microVMs with their own guest kernel, so the seccomp profile's host-kernel role is subsumed by the VM boundary); capability restrictions remain. See [Container runtime](#container-runtime).
|
|
193
197
|
|
|
194
198
|
### Image verification
|
|
@@ -239,6 +243,7 @@ If an old `.harness/` directory exists in your working directory, harness will e
|
|
|
239
243
|
| `--no-context-files` | `-nc` | Disable mounting global context files (`~/.agents/AGENTS.md`, `~/.claude/CLAUDE.md`) |
|
|
240
244
|
| `--ephemeral` | | Disable session persistence (implied by `-p` and piped stdin) |
|
|
241
245
|
| `--local` | | Force local mode even with `-e` (use LM Studio / local defaults) |
|
|
246
|
+
| `--mount-entire-home` | | Allow running from your home directory (mounts all of `$HOME` as the workspace) |
|
|
242
247
|
| `--help` | `-h` | Show help |
|
|
243
248
|
|
|
244
249
|
### Environment variables
|
|
@@ -256,15 +261,27 @@ By default harness runs images with `docker`. On macOS 26 / Apple Silicon you ca
|
|
|
256
261
|
|
|
257
262
|
```bash
|
|
258
263
|
brew install container # install Apple's container CLI (v1.0.0+)
|
|
259
|
-
export HARNESS_CONTAINER_RUNTIME=apple
|
|
260
264
|
container system start # one-time: start the container system service
|
|
265
|
+
container system kernel set --recommended --arch arm64 # one-time on Apple Silicon (required before first run)
|
|
266
|
+
export HARNESS_CONTAINER_RUNTIME=apple
|
|
261
267
|
harness -p "write me a fizzbuzz in Go"
|
|
262
268
|
```
|
|
263
269
|
|
|
270
|
+
On arm64 Macs, `container system kernel set --recommended` is required — without it the first `harness` run fails with `default kernel not configured for architecture arm64`. See [apple/container](https://github.com/apple/container) for details.
|
|
271
|
+
|
|
264
272
|
The value is **named, not boolean** (`apple` or `docker`, case-insensitive); any other value is a hard error. harness never auto-detects the runtime — you must opt in. Image verification (cosign + SLSA provenance) works identically under both runtimes; the verified-digest cache is keyed by digest, so a digest verified under one runtime is a cache hit under the other.
|
|
265
273
|
|
|
266
274
|
**Security note.** Under `=apple`, harness does not apply the `--security-opt no-new-privileges` and `--security-opt seccomp=...` flags it uses under docker, because `apple/container` has no `--security-opt` option. This is not a security regression: each apple/container workload is a microVM with its own ephemeral guest kernel (Apple Virtualization framework), so the `block-af-alg.json` profile's host-kernel role — blocking `socket(AF_ALG)` — is subsumed by the VM boundary itself (hardware-assisted isolation, strictly stronger than a syscall filter). Capability restrictions (`--cap-drop=ALL --cap-add=NET_RAW`) **are** supported and stay on. Only `ro`/`readonly` is honored for volume options under the apple path (SELinux relabel flags like `:Z` are meaningless under macOS virtiofs).
|
|
267
275
|
|
|
276
|
+
**Optional tuning — resource limits.** Apple's container CLI reads defaults from `~/.config/container/config.toml`. Create this file to control CPU and memory allocation for all containers (see [container-system-config.md](https://github.com/apple/container/blob/main/docs/container-system-config.md) for the full schema):
|
|
277
|
+
|
|
278
|
+
```toml
|
|
279
|
+
# example values; adjust to your machine
|
|
280
|
+
[container]
|
|
281
|
+
cpus = 8
|
|
282
|
+
memory = "4g"
|
|
283
|
+
```
|
|
284
|
+
|
|
268
285
|
### Agent-specific behavior
|
|
269
286
|
|
|
270
287
|
- **pi** — `-m` is passed straight to the binary as `--model`.
|
package/bin/harness.js
CHANGED
|
@@ -365,6 +365,7 @@ Options:
|
|
|
365
365
|
--no-context-files Disable mounting global context files (~/.agents/AGENTS.md, ~/.claude/CLAUDE.md); alias -nc
|
|
366
366
|
--ephemeral Disable session persistence (implied by -p and piped stdin)
|
|
367
367
|
--local Force local mode even with -e (use LM Studio / local defaults)
|
|
368
|
+
--mount-entire-home Allow running from your home directory (mounts all of $HOME as the workspace)
|
|
368
369
|
-h, --help Show this help message
|
|
369
370
|
|
|
370
371
|
Environment variables:
|
|
@@ -402,7 +403,7 @@ function getImage(agent) {
|
|
|
402
403
|
return `${REGISTRY}:${tag}`;
|
|
403
404
|
}
|
|
404
405
|
const MINIMIST_OPTS = {
|
|
405
|
-
boolean: ["help", "h", "ephemeral", "local"],
|
|
406
|
+
boolean: ["help", "h", "ephemeral", "local", "mount-entire-home"],
|
|
406
407
|
string: [
|
|
407
408
|
"env-file",
|
|
408
409
|
"e",
|
|
@@ -457,6 +458,7 @@ if (argv.help) {
|
|
|
457
458
|
const noVerify = argv.verify === false;
|
|
458
459
|
const noSkills = argv.skills === false;
|
|
459
460
|
const noContextFiles = argv["context-files"] === false;
|
|
461
|
+
const mountEntireHome = argv["mount-entire-home"] === true;
|
|
460
462
|
const localMode = argv.local;
|
|
461
463
|
const envFilePath = argv["env-file"] || null;
|
|
462
464
|
const fileArg = argv.file || null;
|
|
@@ -483,6 +485,19 @@ if (fileArg && node_fs_1.default.statSync(fileArg).isDirectory()) {
|
|
|
483
485
|
console.error(`harness: --file requires a file, not a directory: ${fileArg}`);
|
|
484
486
|
process.exit(1);
|
|
485
487
|
}
|
|
488
|
+
// Guard against accidentally mounting the entire home directory as the
|
|
489
|
+
// workspace. Running from $HOME mounts all dotfiles/credentials into the
|
|
490
|
+
// container and gives the agent a noisy, unfocused workspace. Only relevant
|
|
491
|
+
// when the cwd is what gets mounted (not --file mode). --mount-entire-home
|
|
492
|
+
// is the explicit opt-in for the rare case where this is intended.
|
|
493
|
+
if (!fileArg && !mountEntireHome) {
|
|
494
|
+
const resolvedCwd = node_fs_1.default.realpathSync(workspace);
|
|
495
|
+
const resolvedHome = node_fs_1.default.realpathSync(node_os_1.default.homedir());
|
|
496
|
+
if (resolvedCwd === resolvedHome) {
|
|
497
|
+
console.error(`harness: refusing to run from your home directory (${resolvedHome}).\nThis would mount your entire home into the container, exposing dotfiles and credentials.\nPass --mount-entire-home to proceed, or cd into a project directory first.`);
|
|
498
|
+
process.exit(1);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
486
501
|
const volumeArgList = Array.isArray(argv.volumes)
|
|
487
502
|
? argv.volumes
|
|
488
503
|
: argv.volumes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@boldblackai/harness",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.2",
|
|
4
4
|
"description": "Portable environment for running coding agents in a container",
|
|
5
5
|
"bin": {
|
|
6
6
|
"harness": "bin/harness.js"
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"lint:docker": "hadolint Dockerfile Dockerfile.opencode Dockerfile.hermes",
|
|
26
26
|
"lint:actions": "actionlint",
|
|
27
27
|
"format": "biome format --write .",
|
|
28
|
-
"test:e2e:cli": "node --test tests/e2e
|
|
29
|
-
"test:e2e": "node --test tests/e2e
|
|
30
|
-
"test:coverage": "node --experimental-test-coverage --test-coverage-include=\"src/**\" --test-coverage-lines=80 --test-coverage-functions=80 --test-coverage-branches=80 tests/e2e
|
|
28
|
+
"test:e2e:cli": "node --test tests/e2e/*.test.mjs",
|
|
29
|
+
"test:e2e": "node --test tests/e2e/*.test.mjs",
|
|
30
|
+
"test:coverage": "node --experimental-test-coverage --test-coverage-include=\"src/**\" --test-coverage-lines=80 --test-coverage-functions=80 --test-coverage-branches=80 tests/e2e/*.test.mjs"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"minimist": "^1.2.8"
|