Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .ai/skills/hack-repo-verify/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ untracked candidate additions. Keep private artifacts excluded from the index.
| Native authored command slices | `tests/native-authored-command.test.ts` (unsupported matrix), `tests/native-authored-command-dispatch.test.ts`, `tests/native-authored-project-{start,run,observe,logs,exec,recovery}.test.ts` and the [command contract](../../../docs/reference/native-authored-commands.md); the dispatch "source CLI" cases need the prepared compiler (`bun run build:config-compiler` or absolute `HACK_CONFIG_COMPILER_BINARY`) and fail without it; `native-authored-project-start` and `native-authored-project-exec` are nondeterministic on a local macOS host, so a single local pass or failure is not proof; live acceptance runs host-serialized on one prepared macOS provider slot |
| Native VM/routing/reclamation | [candidate guide](../../../docs/guides/native-candidate.md), relevant acceptance gate and isolated host fixture; Linux checks cannot qualify macOS effects |
| Resource/performance claims | [performance skill](../hack-repo-performance/SKILL.md), matched workload and measured boundaries |
| Bun runtime crashes | Read-only history: `bun run ci:bun-crash-census -- --since <UTC> --until <UTC>` (failed `test` job logs; `--scan-passing` for all; since the suite moved to `test shard i/4` jobs, pass `--job "test shard i/4"` once per shard for newer runs). Matched reproduction: the gated [experiment](../../../.github/workflows/bun-crash-experiment.yml), which spends hosted macOS minutes and needs an explicit go-ahead; never loop on a shared host. Its default `focused` mode loops `tests/native-https-owner.test.ts` (where both original segfaults happened); `full` needs a budget of at least 40 minutes because one full suite takes 30-35. A run never starts unless its estimate fits the remaining budget, so read `summary.json` for the verdict and per-mode crash, failure and skipped-for-budget counts. Both share [the classifier](../../../scripts/lib/bun-crash-classify.ts) |
| Bun runtime crashes | Read-only history: `bun run ci:bun-crash-census -- --since <UTC> --until <UTC>` (failed test-suite job logs: every `test shard i/n` job, or the `test` job of a run from before the split, since `test` is now the logless fan-in; `--scan-passing` for all; `--job NAME` selects by exact name or a `*` pattern such as `"test shard *"`; a `warning:` line means no job matched or a selected job ran no Bun, so its zero is not evidence). Matched reproduction: the gated [experiment](../../../.github/workflows/bun-crash-experiment.yml), which spends hosted macOS minutes and needs an explicit go-ahead; never loop on a shared host. Its default `focused` mode loops `tests/native-https-owner.test.ts` (where both original segfaults happened); `full` needs a budget of at least 40 minutes because one full suite takes 30-35. A run never starts unless its estimate fits the remaining budget, so read `summary.json` for the verdict and per-mode crash, failure and skipped-for-budget counts. Both share [the classifier](../../../scripts/lib/bun-crash-classify.ts) |

Read [CI](../../../.github/workflows/ci.yml) for current hosted gates. A local pass
does not mean CI ran.
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/ax-target-result-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ WU21). The inspector itself only produces snapshots.
| Malformed Hack config or Compose file, oversized input | `config_parse_failed`, `compose_parse_failed`, `generation_input_too_large` | same | paired fixture |
| Unreadable input (directory in place of a file, no read permission) | `input_unreadable`, `input_permission_denied` | same | paired fixture (permission case skipped as root) |
| Input guard refusals (native/legacy conflict, unsafe adoption state, non-directory project dir) | `project_inputs_unsupported`, `adoption_state_unsafe`, `input_unreadable` | same | paired fixture |
| Unsearchable `.hack` or `.hack/.internal` on a never-adopted project | `input_permission_denied` (permission fix named first) | same | paired fixture and domain test, each at mode `000` and `0644` on both directories (skipped as root; the message is checked unconditionally) |
| Unsearchable `.hack` or `.hack/.internal` on a never-adopted project | `input_permission_denied` (permission fix named first) | same | paired fixture and domain test, each at mode `000` and `0644` on both directories, plus a paired `.hack` at `000` with no `.hack/.internal` (skipped as root; the message is checked unconditionally) |
| Uncheckable input presence (`.dev` file or unsearchable `.dev` beside a valid `.hack`) | `input_unreadable`, `input_permission_denied` | same | paired fixture and domain test (unsearchable case skipped as root; `EPERM` through the classifier seam and an injected presence-check fault) |
| Detached linked worktree, no project, missing path | closed refusals | same | paired fixture |
| Unknown service | `service_not_found` | same | paired fixture |
Expand Down
42 changes: 28 additions & 14 deletions docs/reference/ax-task-catalog.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ is not implemented here.
wrappers (`npx -c`, `npm exec -c`, `pnpm -c`, `yarn exec`, `bun exec`,
`nodemon --exec`, `tsx -e`, `ts-node -e`, `julia -e`, `sudo -s`,
`doas -s`, `git -c`/`--config`/`--exec`/`--upload-pack`/
`--receive-pack`/`clone -u`/`submodule foreach`/`bisect`/
`--receive-pack`/`--cmd`/`clone -u`/`submodule foreach`/`bisect`/
`filter-branch`, a Git `!` alias value such as
`git config alias.x '!cmd'`, `make --eval`, `nix eval`). A dashed Git
builtin, by basename or absolute path (`git-clone`,
Expand All @@ -97,9 +97,12 @@ is not implemented here.
(`psql -1c`, `mysql -Be`). For `psql`, `mysql`, `mariadb` and Python,
a cluster is read only up to the first option that takes a value, so
`psql -hlocalhost`, `psql -Ucustomer`, `mysql -uroot -psecret` and
`python -mcompileall` pass. `clone -u` counts when any operand is
`clone`, so a Git global option that takes a value
`python -mcompileall` pass. `clone -u` counts after any operand that
is `clone`, so a Git global option that takes a value
(`git --attr-source HEAD clone -u cmd`) cannot hide the subcommand.
It does not count before that operand, because Git reads a
subcommand's options only after its name, so
`git push -u origin clone` (a branch named `clone`) passes.
Accepted forms include `psql -f script.sql`,
`mysql --batch db`, `sqlite3 -csv app.db`,
`duckdb -f script.sql data.duckdb`,
Expand Down Expand Up @@ -358,6 +361,8 @@ until its recipes are re-reviewed.
| `psql -o`/`--output` piped to a command (`-o '\|cmd'`, `-o\|cmd`, `-qo\|cmd`, `--output=\|cmd`, `--out=\|cmd`) | `inline_command`; `psql -o out.txt`, `psql -F'\|'`, `psql -L '\|log'` (a file name) still pass | domain test |
| Dashed Git builtins (`/usr/lib/git-core/git-clone -u cmd`, `git-config alias.x '!cmd'`, `git-ls-remote --upload-pack=cmd`, `git-fetch-pack --exec=cmd`) | `inline_command`; `git-clone url dir`, `git-push -u origin main`, `git-config alias.st status` still pass | domain test |
| Git plumbing helpers read as their porcelain (`git-bisect--helper --bisect-run cmd`, `/usr/lib/git-core/git-bisect--helper.exe run cmd`, `git bisect--helper --bisect-run cmd`, `git-submodule--helper foreach cmd`, `git-config alias.x bisect--helper`, older Git's `git rebase--helper --add-exec-commands cmd` in any letter case) | `inline_command`; `git-submodule--helper status`, `git-difftool--helper a b`, `git add bisect--helper.txt`, `git config --add alias.st status` still pass | domain test |
| Git 2.20-2.33's `rebase--interactive --cmd cmd` (`git rebase--interactive --cmd=cmd`, `git-rebase--interactive --cmd cmd`, `--cm cmd`, a repository alias such as `git ri --cmd cmd`) and its `--add-exec-commands` mode | `inline_command`; `git rebase--interactive --continue`, `git-rebase--interactive --edit-todo` still pass | domain test |
| A subcommand-scoped Git flag before the subcommand's name (`git push -u origin clone`, `git remote set-branches --add origin rebase`, `git config --add alias.r rebase`) | accepted; the flag after the name (`git clone -u cmd url`, `git --unknown v clone -u cmd url`, `git rebase--helper --add cmd`) is still refused | domain test |
| Detached linked worktree with no named instance, on CLI `task list`/`task inspect` and MCP `hack.project.task.list`/`inspect` | `detached_checkout`; naming the instance (`--branch`, MCP `branch`) passes | paired fixture, domain test |
| `duckdb -storage-version VER db` | accepted; a SQL operand after the database is still refused | domain test |
| `git clone -u` (also after `--attr-source <tree>` or an unknown global option), abbreviated or attached `--upload-pack`/`--receive-pack`/`--exec`, `clone --config`, and `!` alias values (`git config alias.x '!cmd'`, `git -c alias.x=!cmd`) | `inline_command`; `git clone url dir`, `git push -u origin main`, `git add -u`, `git config alias.st status` still pass | domain test |
Expand Down Expand Up @@ -401,7 +406,7 @@ until its recipes are re-reviewed.
reviewer must check argv, and WU26 execution must not rely on this check
as a sandbox. For a known program it is fail-closed, so it can also refuse
an ordinary argument that looks like an inline flag (`script.sh -c`,
`git clean -x`, `git -C dir status`, `git push -u origin clone`,
`git clean -x`, `git -C dir status`, `git log -S clone -u`,
`make -e`, `python -I`, `mysql -E`, a Git argument such as `--grep=!x`
that starts a value with `!`, or a script argument named like a REPL
module after a Python trampoline, as in
Expand All @@ -417,16 +422,25 @@ until its recipes are re-reviewed.
string in argv is. Code a program loads from a file is not inspected
either: for example, a reviewed script that loads a data file with
`pickle` or `marshal`, or reads a config file that it evaluates. A Git
`<sub>--helper` plumbing command (`git-bisect--helper`,
`git submodule--helper`) is read as `git <sub>`, so it meets the rules for
that subcommand. A helper's own options are modeled only where they run a
command: older Git's `rebase--helper --add-exec-commands` is refused
through a flag scoped to `rebase`. Any whole-word `<sub>--helper`
argument is read as `<sub>` (fail-closed), so
`git push -u origin clone--helper` (a branch of that name) and
`git add bisect--helper` (a file of that name) are refused. The `rebase`-scoped flag counts when any
operand is `rebase`, and Git abbreviates, so
`git config --add alias.r rebase` is refused too. Git config writes other than `!` aliases (for example
`<sub>--helper` or `<sub>--interactive` plumbing command
(`git-bisect--helper`, `git submodule--helper`, Git 2.20-2.33's
`git rebase--interactive`) is read as `git <sub>`, so it meets the rules
for that subcommand. A helper's own options are modeled only where they
run a command: Git 2.13-2.19's `rebase--helper --add-exec-commands` is
refused through a flag scoped to `rebase`, and `rebase--interactive
--cmd` through `--cmd`, which is refused under every subcommand because
a repository alias can rename the helper. No Git option shares its
`--cm` prefix, so only an argument spelled like it, such as
`git grep -e --cmd`, is refused with it. Any whole-word `<sub>--helper` or `<sub>--interactive`
argument is read as `<sub>` (fail-closed), so `git add bisect--helper`
(a file of that name) is refused. A subcommand-scoped flag (`clone -u`,
`rebase --add-exec-commands`) counts after any operand that names the
subcommand, and Git abbreviates, so an earlier word `clone` or `rebase`
scopes it even when that word is another option's value:
`git commit -m rebase --add` and `git log -S clone -u` (`-u` is
`--patch`) are refused. The same flag before the word passes
(`git config --add alias.r rebase`,
`git remote set-branches --add origin rebase`). Git config writes other than `!` aliases (for example
`git config core.pager cmd` or `include.path`) are not detected. Nor are
`mysql`/`mariadb` option files named in argv (`--defaults-extra-file`,
which can set `init-command`) or `--pager`, which runs a command only in
Expand Down
4 changes: 2 additions & 2 deletions docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -1037,7 +1037,7 @@ hack global <subcommand> [options]
| `hack global ca` | Export Caddy Local CA cert (print path or PEM) |
| `hack global cert <hosts...>` | Generate local TLS certs via mkcert (for non-Caddy services) |
| `hack global trust` | Trust Caddy Local CA (macOS) so https://*.hack is trusted |
| `hack global logs-reset` | Wipe Loki/Grafana volumes (fresh logs + dashboards) |
| `hack global logs-reset` | Wipe Loki, Grafana and Alloy volumes (fresh logs, dashboards and Alloy read positions) |
| `hack global authorize` | Authorize passwordless DNS recovery commands on macOS |

### Options
Expand Down Expand Up @@ -1242,7 +1242,7 @@ hack global trust [options]

## `hack global logs-reset`

Wipe Loki/Grafana volumes (fresh logs + dashboards)
Wipe Loki, Grafana and Alloy volumes (fresh logs, dashboards and Alloy read positions)

### Usage

Expand Down
7 changes: 4 additions & 3 deletions docs/reference/shared-host-lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,10 @@ Progress events are also closed: `lock-waiting`, `lock-acquired`, `claim-stale`,

`bun test tests/shared-host-lifecycle.test.ts` runs portable local fixtures. Each
fixture is a Bun process on a loopback port with a private HOME. No Docker, real
HOME, keychain or privileged effect is involved. The file runs in the hosted macOS
`test` job and is registered in the hosted Linux `linux-process-lifetime` job. A
Linux pass is a receipt only once that hosted job has run it.
HOME, keychain or privileged effect is involved. The file runs in one of the hosted
macOS `test shard i/4` jobs (the required `test` check passes only when every shard
passes) and is registered in the hosted Linux `linux-process-lifetime` job. A Linux
pass is a receipt only once that hosted job has run it.

| Scenario | Asserted behavior |
| --- | --- |
Expand Down
2 changes: 1 addition & 1 deletion docs/security/env-secrets-threat-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ refer to `hack-dance/hack`.
| A key copy ships in build output or a static site. | `hack doctor` reports `env-key-public-path` for copies under `public/`, `static/`, `dist/`, `build/`, `out/`, `www/`, `_site/`, `.next/` and `.output/` (#309). |
| A weak, short or repetitive key. | `env-key-wrong-length` and `env-key-low-entropy` (#309). Generated keys are 32 random bytes in base64url. |
| Materialized `.hack/.env` or local overrides are committed. | Hack owns a committed `.hack/.gitignore` block, and `hack doctor` flags tracked generated files (see [ignore rules](../env.md#ignore-rules-committed-hackgitignore)). |
| A secret leaks into a public artifact through a path nobody listed. | `tests/env-secret-sentinel-scan.test.ts` runs in the CI `test` job. It gives a primary checkout and a linked worktree random synthetic secrets through `env add --secret --value-stdin` (shared and `--local`), runs materialize, exec, up (Compose generation), env apply, logs, list, explain and doctor, then commits everything with `git add -A`. It requires zero occurrences of any value or the project key, raw or hex/base64/base64url encoded, in CLI output, recorded child argv, tracked files, every Git object, HACK_HOME, the private HOME and its caches, and generated Compose and runtime files. Plaintext may exist only in `.hack/.env`, and key text only in key files; each must be git-ignored, untracked and mode 0600. `.hack/.internal/compose.env.override.yml` must hold `${HACK_ENV_...}` references and no value, and must also be private and ignored. Values may reach a child's environment only for the exec'd command and `docker compose`. Positive controls prove delivery and that the scanner finds planted encodings in files and Git objects. Docker is a recording shim; no engine, keychain or installed Hack is reached. |
| A secret leaks into a public artifact through a path nobody listed. | `tests/env-secret-sentinel-scan.test.ts` runs in one of the hosted CI `test shard i/4` jobs; the required `test` check passes only when every shard passes. It gives a primary checkout and a linked worktree random synthetic secrets through `env add --secret --value-stdin` (shared and `--local`), runs materialize, exec, up (Compose generation), env apply, logs, list, explain and doctor, then commits everything with `git add -A`. It requires zero occurrences of any value or the project key, raw or hex/base64/base64url encoded, in CLI output, recorded child argv, tracked files, every Git object, HACK_HOME, the private HOME and its caches, and generated Compose and runtime files. Plaintext may exist only in `.hack/.env`, and key text only in key files; each must be git-ignored, untracked and mode 0600. `.hack/.internal/compose.env.override.yml` must hold `${HACK_ENV_...}` references and no value, and must also be private and ignored. Values may reach a child's environment only for the exec'd command and `docker compose`. Positive controls prove delivery and that the scanner finds planted encodings in files and Git objects. Docker is a recording shim; no engine, keychain or installed Hack is reached. |
| A dependency vulnerability ships in Hack. | `bun install --frozen-lockfile`, and the read-only `scripts/dependabot-inventory.ts` lists open alerts (#309). Hack does not sandbox project dependencies that run under `hack host exec`; they receive the injected env by design. |

### Agent and MCP exfiltration
Expand Down
Loading
Loading