Skip to content
13 changes: 13 additions & 0 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,19 @@ route for it.
- Browser/host URL: use `hack open <service> --json`; OAuth aliases are preferred when enabled.
- Container-to-container traffic: use Compose DNS rather than routing back through Caddy.

For `run`, `exec`, `host exec`, `env exec`, `x` and the Beta `dispatch run`, Hack options go before
the child command.
From the child command's first word on, every argument belongs to the child, including flags
that Hack also defines: `hack run web bun --version` runs `bun --version` in the container
rather than printing Hack's version, and a child `--json` does not switch Hack's own error output
to JSON. A `--` before the child command is optional and is not passed on; a later `--` (as in
`hack run web npm test -- --watch`) reaches the child unchanged.

`session exec` takes the workspace command as one argument, so quote a command that has its own
arguments or flags: `hack session exec ws "npm --version"`. Hack options go before it, and an
unquoted `hack session exec ws npm --version` refuses with a hint to quote the command instead
of printing Hack's version.

Service-scoped runtime changes do not run project-wide lifecycle hooks and do not start Compose
dependencies implicitly, except declared shared-cache installers needed by the selected services:

Expand Down
20 changes: 18 additions & 2 deletions docs/guides/native-candidate.md
Original file line number Diff line number Diff line change
Expand Up @@ -663,9 +663,25 @@ lifecycle hooks, and publishes the run mapping after native readiness. Ctrl-C wa
for owned cleanup and keeps the stopped run mapping and named volumes after
confirming cleanup. The VM remains available until explicit runtime shutdown.

Without that grant, `up` and `restart` refuse with `E_PERMISSION` before any stop or
runtime request; set it and run the command again.

This initial foreground path does not support detached/JSON startup, selected
services or external networks. Routed services and host dependencies require the
explicit native selections described below. Existing
services or external networks. `--detach`, `--json`, `--target` and service names
each refuse with a one-line `E_USAGE` message that names the option and the supported
form: run `hack up` in the foreground in its own terminal and use `hack ps` and
`hack logs` from another. Routed services and host dependencies require the
explicit native selections described below. Unsupported Compose or environment input
(for example a service `env_file`, or a project `.env` without managed env) and
unreviewed route labels refuse with `E_NATIVE_PROJECT_UNSUPPORTED`; invalid branch
hosts, project adaptations, AWS selections and changed fresh-exec environments refuse
with `E_CONFIG_INVALID`, as do an unbounded `--profile` selection, a malformed
`HACK_NATIVE_DEPENDENCIES` listener selection and an occupied HTTPS port. A runtime
reply, saved mapping or retained graph that changed or cannot be confirmed (for example
a startup receipt for another run, or a mapping owned by another run) is an integrity
or ownership refusal, not a configuration one: it prints one closed line naming what
was not confirmed, and `--json` reports it as `E_UNEXPECTED`. Each prints one closed
line and drops nothing. Existing
`hack.dependencies.*` cache declarations publish the reviewed source snapshot and
require successful initializer completion before dependent services start. Cache
initializers receive immutable source mounts and writable named cache volumes;
Expand Down
24 changes: 18 additions & 6 deletions docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,9 +260,11 @@ Run a one-off command in a service container (docker compose run --rm)
### Usage

```bash
hack run <service> [cmd...] [options]
hack run [options] <service> [cmd...]
```

Everything from `<cmd>` on is passed to the command unchanged, including flags such as `--version` or `--help`. Put Hack options before it; a leading `--` is optional.

### Arguments

| Arg | Description |
Expand Down Expand Up @@ -292,9 +294,11 @@ Run a command in an already-running service container (docker compose exec)
### Usage

```bash
hack exec <service> [cmd...] [options]
hack exec [options] <service> [cmd...]
```

Everything from `<cmd>` on is passed to the command unchanged, including flags such as `--version` or `--help`. Put Hack options before it; a leading `--` is optional.

### Arguments

| Arg | Description |
Expand Down Expand Up @@ -841,9 +845,11 @@ Send a command to a running workspace
### Usage

```bash
hack session exec <workspace> <command> [options]
hack session exec [options] <workspace> <command>
```

`<command>` is one argument: quote a command that has its own arguments or flags, as in `"npm --version"`. Put Hack options before it; a leading `--` is optional.

Queue a command in the workspace without opening a new interactive attach flow. Tmux sends it to the active pane; zellij opens a new pane for the command. This is useful for long-running agents, background checks, or remote follow-up work.

### Arguments
Expand Down Expand Up @@ -2478,9 +2484,11 @@ Run a host command with project env injected
### Usage

```bash
hack env exec [command...] [options]
hack env exec [options] [command...]
```

Everything from `<command>` on is passed to the command unchanged, including flags such as `--version` or `--help`. Put Hack options before it; a leading `--` is optional.

Inject the selected Hack env overlay directly into a one-off host command without materializing .hack/.env. To inspect a value, prefer `printenv KEY` or `sh -lc 'printf "%s\n" "$KEY"'`; `echo $KEY` expands in your current shell before Hack injects env.

### Arguments
Expand Down Expand Up @@ -2640,9 +2648,11 @@ Run a host command with project env injected
### Usage

```bash
hack host exec [command...] [options]
hack host exec [options] [command...]
```

Everything from `<command>` on is passed to the command unchanged, including flags such as `--version` or `--help`. Put Hack options before it; a leading `--` is optional.

Run a one-off command on the host with the selected Hack env overlay injected. Use --scope when you want service-scoped values without running inside that service container. To inspect a value, prefer `printenv KEY` or `sh -lc 'printf "%s\n" "$KEY"'`; `echo $KEY` expands in your current shell before Hack injects env.

### Arguments
Expand Down Expand Up @@ -2702,9 +2712,11 @@ Run extension commands
### Usage

```bash
hack x [args...] [options]
hack x [options] [args...]
```

Everything from `<args>` on is passed to the command unchanged, including flags such as `--version` or `--help`. Put Hack options before it; a leading `--` is optional.

Usage:
hack x list
hack x <namespace> help
Expand Down
27 changes: 22 additions & 5 deletions docs/reference/native-authored-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,21 @@ Ctrl-C requests that shutdown. Exact durable Removed evidence retires the tagged
native run and startup intent. Unknown, changed or live cleanup retains the attempt
and refuses replay; a missing ready mapping does not authorize a fresh start.

On macOS, `up --detach` and `restart --detach` refuse with `E_USAGE` and the same
one-line message as an image project: native `up` runs in the foreground only, so run
it without `--detach` in its own terminal, use `ps` and `logs` from another terminal,
and press Ctrl-C to stop it.

After native review and before it reserves the startup intent, `up` asks the runtime
for its read-only status. A stopped runtime (any phase other than `running`, or a dead
runtime process) refuses at stage `reservation` with native code `runtime_not_running`.
The CLI reports `E_DOCKER_UNAVAILABLE`: "Native runtime is not running; nothing was
started." No intent is retained, so `ps` keeps reporting `not_started`. An inconclusive
status reply does not refuse. The check is advisory: if the runtime stops after it,
the start fails later and, when its intent was already reserved, keeps that evidence;
the CLI then still reports `E_DOCKER_UNAVAILABLE`, and `ps` reports `pending` until the
retained attempt is inspected.

## Saved-run `ps`

`ps` and `ps --json` issue one authenticated current status request for the exact
Expand All @@ -122,10 +137,11 @@ Only fresh runtime observations supply state and health. Stored readiness and ph
do not prove that a service is still live.

A project with no saved attempt reports `not_started`; a retained startup intent
without a Ready mapping reports `pending` with no confirmed service rows. That
state covers a startup in progress and a retained incomplete attempt. When no
startup is running, `down --recover` can retire the attempt if it never reserved
runtime state (see below). When a
without a Ready mapping reports `pending` with no confirmed service rows. `pending`
covers a start that is still in progress and a failed start whose reserved intent was
retained as evidence; Hack does not guess which, because only the owner's cleanup proof
may retire that intent. When no startup is running, `down --recover` can retire the
attempt if it never reserved runtime state (see below). When a
Ready generation is saved but the candidate runtime is stopped (native code
`runtime_not_running`, or the authenticated `native_graph_foreground` control code
with that exact cause), `ps` exits 0 and reports `runtime_not_running` with the
Expand Down Expand Up @@ -272,7 +288,8 @@ half is `up`, `host.up.before`, `host.up.after` and `host.processes` run again
exactly as on `up`, after the stopped generation's `host.down.before`,
`host.down.after` and process stop completed. Startup refusals keep the `up`
contract: a fixed owning stage, a closed compiler or native code and
`E_STARTUP_INCOMPLETE` or `E_NATIVE_PROJECT_UNSUPPORTED`. Retained startup evidence
`E_STARTUP_INCOMPLETE`, `E_NATIVE_PROJECT_UNSUPPORTED` or, for a stopped runtime,
`E_DOCKER_UNAVAILABLE`. Retained startup evidence
from an incomplete retirement refuses the fresh start without replay. A Ctrl-C that
arrives after the proven stop and before the fresh start returns 130 with nothing
restarted. Each half uses the configured `HACK_COMPOSE_STARTUP_TIMEOUT_MS` budget
Expand Down
8 changes: 6 additions & 2 deletions scripts/check-native-acquisition-plan-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,9 +270,13 @@ try {
assert(
runtime.exit !== 0 &&
(runtime.stdout + runtime.stderr).includes(
"E_NATIVE_PROJECT_UNSUPPORTED"
// On macOS authored foreground up is supported, so --detach gets the
// shared one-line E_USAGE refusal; elsewhere it is unsupported.
process.platform === "darwin"
? "E_USAGE"
: "E_NATIVE_PROJECT_UNSUPPORTED"
),
"unsupported native detached up refuses before input or runtime work"
"native detached up refuses before input or runtime work"
);
assert(!(await Bun.file(marker).exists()), "planning executes no hook");
for (const cwd of [primary, checkout]) {
Expand Down
8 changes: 6 additions & 2 deletions scripts/check-native-endpoint-plan-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,9 +376,13 @@ try {
assert(
runtime.exit !== 0 &&
(runtime.stdout + runtime.stderr).includes(
"E_NATIVE_PROJECT_UNSUPPORTED"
// On macOS authored foreground up is supported, so --detach gets the
// shared one-line E_USAGE refusal; elsewhere it is unsupported.
process.platform === "darwin"
? "E_USAGE"
: "E_NATIVE_PROJECT_UNSUPPORTED"
),
"unsupported native detached up refuses before input or runtime work"
"native detached up refuses before input or runtime work"
);
assert(
!(await Bun.file(marker).exists()),
Expand Down
6 changes: 4 additions & 2 deletions scripts/check-native-env-plan-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,10 @@ try {
});
require(unsupported.exit !== 0 &&
(unsupported.stdout + unsupported.stderr).includes(
"E_NATIVE_PROJECT_UNSUPPORTED"
), "unsupported native detached up refuses before input or runtime work");
// On macOS authored foreground up is supported, so --detach gets the
// shared one-line E_USAGE refusal; elsewhere it is unsupported.
process.platform === "darwin" ? "E_USAGE" : "E_NATIVE_PROJECT_UNSUPPORTED"
), "native detached up refuses before input or runtime work");
require((await readdir(home)).length === 0 &&
!(await readdir(join(checkout, ".hack"))).some(
(name) => name === ".internal" || name === ".branch"
Expand Down
8 changes: 6 additions & 2 deletions scripts/check-native-host-plan-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,9 +283,13 @@ try {
assert(
runtime.exit !== 0 &&
(runtime.stdout + runtime.stderr).includes(
"E_NATIVE_PROJECT_UNSUPPORTED"
// On macOS authored foreground up is supported, so --detach gets the
// shared one-line E_USAGE refusal; elsewhere it is unsupported.
process.platform === "darwin"
? "E_USAGE"
: "E_NATIVE_PROJECT_UNSUPPORTED"
),
"unsupported native detached up refuses before input or runtime work"
"native detached up refuses before input or runtime work"
);
assert(
!(await Bun.file(marker).exists()) &&
Expand Down
8 changes: 6 additions & 2 deletions scripts/check-native-process-plan-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,9 +271,13 @@ try {
assert(
runtime.exit !== 0 &&
(runtime.stdout + runtime.stderr).includes(
"E_NATIVE_PROJECT_UNSUPPORTED"
// On macOS authored foreground up is supported, so --detach gets the
// shared one-line E_USAGE refusal; elsewhere it is unsupported.
process.platform === "darwin"
? "E_USAGE"
: "E_NATIVE_PROJECT_UNSUPPORTED"
),
"unsupported native detached up refuses before input or runtime work"
"native detached up refuses before input or runtime work"
);
assert(
!(await Bun.file(marker).exists()),
Expand Down
8 changes: 6 additions & 2 deletions scripts/check-native-routing-plan-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,9 +384,13 @@ try {
assert(
runtime.exit !== 0 &&
(runtime.stdout + runtime.stderr).includes(
"E_NATIVE_PROJECT_UNSUPPORTED"
// On macOS authored foreground up is supported, so --detach gets the
// shared one-line E_USAGE refusal; elsewhere it is unsupported.
process.platform === "darwin"
? "E_USAGE"
: "E_NATIVE_PROJECT_UNSUPPORTED"
),
"unsupported native detached up refuses before input or runtime work"
"native detached up refuses before input or runtime work"
);
assert(
!(
Expand Down
Loading
Loading