> For the complete documentation index, see [llms.txt](https://docs.stepsecurity.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.stepsecurity.io/github-actions/harden-runner/harden-runner-installation/runner-job-hooks.md).

# Runner Job Hooks

{% hint style="warning" %}
Applies to ARC, self-hosted VM, and GitHub-hosted custom VM scenarios
{% endhint %}

GitHub Actions runners can run a script before a job starts and after it finishes, using the `ACTIONS_RUNNER_HOOK_JOB_STARTED` and `ACTIONS_RUNNER_HOOK_JOB_COMPLETED` environment variables. StepSecurity ships two hook bundles, `pre.js` and `post.js`, that plug into those runner hooks.

The hooks connect an individual job to the Harden-Runner agent installed on the runner. What they do depends on the scenario, so this page covers the shared reasons to use them, then each scenario separately.

The hooks do not install the agent. Install the agent first, then configure the hooks.

### Why use the hooks

The hooks are the recommended configuration for every scenario on this page.

Nothing stops working without them. The Harden-Runner agent keeps monitoring runtime behavior, and runtime insights keep appearing in the StepSecurity dashboard. What the hooks add is:

* **Egress policies from the Policy Store, applied per job, with no workflow file changes.** You attach a policy once in the StepSecurity dashboard and every job on the runner picks it up.
* **The job security summary published into the GitHub run.** Blocked endpoints, detections, and the link to full insights render in the run page itself, so developers see them without opening the StepSecurity dashboard.
* **More consistent per-job correlation.** The hooks give the agent an explicit correlation ID for each job, which makes attribution of runtime activity to a specific workflow run and job more reliable.

{% hint style="warning" %}
Use the hooks, not the Harden-Runner action, for policy enforcement in these scenarios.Enforcing policies through the Harden-Runner action means editing every workflow file, and it is the older path. With the hooks configured on the runner, policies are attached centrally in the StepSecurity dashboard and applied to every job automatically, with no workflow changes at all.
{% endhint %}

### ARC

In the ARC scenario the hooks do two things and nothing else. They do not manage the agent, which runs independently of them.

| Hook              | What it does                                                                                                                                                                                                                    |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Pre-job hook**  | Resolves the Policy Store policy for the repository, workflow, and run, and signals the agent to apply it. In block mode, polls StepSecurity for up to 10s for confirmation that the policy is applied before the job proceeds. |
| **Post-job hook** | Fetches the job security summary and appends it to `$GITHUB_STEP_SUMMARY`, so it renders in the GitHub run page.                                                                                                                |

Policy resolution follows the standard Policy Store [precedence rules](/github-actions/harden-runner/policy-store.md#precedence). A policy attached at the **Cluster** scope applies to every job in the cluster unless a more specific workflow, repository, or organization policy exists.

{% hint style="warning" %}
On ARC, attach a policy in the Policy Store and let the pre-job hook apply it. Do not add the Harden-Runner action to your workflows for policy enforcement.
{% endhint %}

### Self-hosted VM

In the VM scenarios the hooks also drive the agent around each job, in addition to policy enforcement and the job summary.

#### Linux

The hook behavior differs slightly depending on whether the runner is persistent or ephemeral.

| Hook              | Persistent runner                                                                                                                                                                | Ephemeral runner                                                                                              |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| **Pre-job hook**  | Writes the job context into the agent configuration, restarts the agent service, waits up to 5s for the agent to report ready, and resolves the Policy Store policy for the job. | Signals the correlation ID to the running agent, then resolves the policy and signals the agent to apply it.  |
| **Post-job hook** | Stops the agent service, publishes the job security summary, prints the agent log into the job log, and removes per-job state files.                                             | Signals job end, waits for the agent to finish, publishes the job security summary, and prints the agent log. |

#### Windows

| Hook              | What it does                                                                                                               |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------- |
| **Pre-job hook**  | Resolves the Policy Store policy for the job and waits up to 60s for the agent to confirm enforcement.                     |
| **Post-job hook** | Signals the agent to clean up after the job and waits up to 60s for confirmation. Does not publish a job security summary. |

{% hint style="info" %}
On Windows self-hosted VM runners the post-job hook does not publish a job security summary. Runtime insights for those jobs remain available in the StepSecurity dashboard.
{% endhint %}

### GitHub-hosted custom VM

On a custom VM image the agent is baked into the image with placeholders in its configuration. The pre-job hook fills those in and starts the agent for the job.

#### Linux

<table><thead><tr><th width="162.6241455078125">Hook</th><th>What it does</th></tr></thead><tbody><tr><td><strong>Pre-job hook</strong></td><td>Fills the job context into the agent configuration, starts the agent service, waits for it to initialize, then resolves the Policy Store policy and signals the agent to apply it.</td></tr><tr><td><strong>Post-job hook</strong></td><td>Signals job completion, waits up to 10s for the agent to finalize its data, prints the agent log into the job log, and publishes the job security summary.</td></tr></tbody></table>

#### Windows

<table><thead><tr><th width="158.9739990234375">Hook</th><th>What it does</th></tr></thead><tbody><tr><td><strong>Pre-job hook</strong></td><td>Fills the job context into the agent configuration, starts the agent service, waits for it to initialize, resolves the Policy Store policy, and waits up to 60s for the agent to confirm enforcement.</td></tr><tr><td><strong>Post-job hook</strong></td><td>Signals job completion, waits for finalization, prints the agent log, publishes the job security summary, stops the agent service, and resets the agent configuration for the next job.</td></tr></tbody></table>

### Platform support

The hook bundles support Linux and Windows runners.

{% hint style="info" %}
macOS runners are not currently supported. Support is on the roadmap.
{% endhint %}

### Requirements

* The **Harden-Runner agent** is installed on the runner. See [Harden Runner Installation](/github-actions/harden-runner/harden-runner-installation.md).
* **Node.js** is installed on the runner and available on `PATH` as `node`. The hook bundles are Node scripts.
* The runner can reach the StepSecurity API endpoints listed under [Configuration](#configuration).
* If your wrapper scripts elevate to run the hooks, `sudo` is available on the runner.

### Setup

Full setup instructions for your scenario, including the ARC runner scale set values and the VM runner service configuration, are provided in the StepSecurity dashboard. If they are not enabled in your account, [contact us](https://www.stepsecurity.io/contact).

The shape of the setup is the same in every scenario:

1. Download `pre.js` and `post.js` from a [StepSecurity job-hook release](https://github.com/step-security/gha-runner-job-hooks/releases).
2. Place them on the runner, along with two small wrapper scripts that set the hook environment variables and invoke the bundles with `node`.
3. Point `ACTIONS_RUNNER_HOOK_JOB_STARTED` at the pre-job wrapper and `ACTIONS_RUNNER_HOOK_JOB_COMPLETED` at the post-job wrapper.

Pin to a release tag rather than tracking the default branch. Each release publishes a `checksums.txt` asset you can use to verify the bundles you downloaded.

### Configuration

The hook bundles are configured entirely through environment variables, so one published bundle works across environments. Set these in the wrapper scripts, or in the runner service environment if they should apply to every job on the runner.

| Variable                  | Default                                   | Description                                                                               |
| ------------------------- | ----------------------------------------- | ----------------------------------------------------------------------------------------- |
| `STEP_API`                | `https://agent.api.stepsecurity.io/v1`    | StepSecurity API base URL. `STEPSECURITY_API` is accepted as an alias.                    |
| `STEP_TELEMETRY_URL`      | `https://prod.app-api.stepsecurity.io/v1` | Telemetry endpoint written into the agent configuration when it is not already set.       |
| `STEP_AGENT_ROOT`         | `/home/agent`                             | Agent directory on Linux, holding `agent.json`, `agent.status`, `done.json`, `agent.log`. |
| `STEP_AGENT_ROOT_WINDOWS` | `C:\agent`                                | Agent directory on Windows.                                                               |
| `STEP_HOOK_MODE`          | `vm`                                      | Selects the hook variant: `vm`, `k8s` (Linux only), or `custom-vm`.                       |

Set only the agent root that matches the runner OS. `STEP_API` and `STEP_TELEMETRY_URL` are the values to override for non-production StepSecurity environments.

The runner hook variables themselves:

| Variable                            | Value                               |
| ----------------------------------- | ----------------------------------- |
| `ACTIONS_RUNNER_HOOK_JOB_STARTED`   | Path to the pre-job wrapper script  |
| `ACTIONS_RUNNER_HOOK_JOB_COMPLETED` | Path to the post-job wrapper script |

#### Hook mode

`STEP_HOOK_MODE` tells the hook which scenario it is running in. Set it explicitly:

| Scenario                | `STEP_HOOK_MODE` |
| ----------------------- | ---------------- |
| ARC                     | `k8s`            |
| Self-hosted VM          | `vm`             |
| GitHub-hosted custom VM | `custom-vm`      |

On Linux, if `STEP_HOOK_MODE` is not one of these values, the hook detects Kubernetes from the pod environment and otherwise treats the runner as a VM. On Windows, `vm` selects the self-hosted flow and `custom-vm` selects the custom image flow; if neither is set, the hook infers the flow from the agent configuration. An explicit value always wins, so set it rather than relying on detection. `k8s` is Linux only.

### Verifying the hooks are running

Both hooks log to the job log with a `[StepSecurity]` prefix. In a job that ran with the hooks configured, expect:

* `[StepSecurity] pre job-hook` and `[StepSecurity] JobHook version=<version>` at the start of the job, and the matching `post job-hook` lines at the end.
* Either `Policy found, applying policy...` or `No policy configured from Policy Store`, which tells you whether a Policy Store policy resolved for the job.
* A line reporting the summary result, in the scenarios that publish one.
* A StepSecurity section in the GitHub job summary for the run.

If no `[StepSecurity]` hook lines appear at all, the runner is not invoking the hooks. Check that both `ACTIONS_RUNNER_HOOK_*` variables are set in the runner's environment and that the wrapper scripts are executable.

### Behavior on failure

The hooks are designed never to fail a workflow job. If a hook cannot reach the StepSecurity API, cannot find the agent, or hits an unexpected error, it logs the problem and exits successfully, and the job continues.

Every wait in the hooks is bounded, so a StepSecurity-side problem cannot hang a job indefinitely:

| Wait                                  | Linux                                                       | Windows                                                                |
| ------------------------------------- | ----------------------------------------------------------- | ---------------------------------------------------------------------- |
| Policy Store request                  | 5s per attempt, up to 4 attempts                            | 5s, single attempt                                                     |
| Confirmation that a policy is applied | Up to 10s (ARC)                                             | Up to 60s                                                              |
| Agent ready after start or restart    | Up to 5s (self-hosted VM, persistent), up to 9s (custom VM) | Up to 30s (custom VM)                                                  |
| Agent finalization after the job      | Up to 5s to 10s                                             | Up to 10s (custom VM), up to 60s cleanup confirmation (self-hosted VM) |
| Job summary request                   | 5s per attempt, up to 4 attempts                            | 10s, single attempt                                                    |

{% hint style="warning" %}
Because the pre-job hook continues once its bound expires, a job can start before enforcement has been confirmed. Treat repeated timeout lines in the job log as a signal to investigate agent health or runner connectivity rather than as noise.
{% endhint %}

### Troubleshooting

| Symptom                                                            | What to check                                                                                                                                                            |
| ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| No `[StepSecurity]` hook lines in the job log                      | The `ACTIONS_RUNNER_HOOK_JOB_STARTED` and `ACTIONS_RUNNER_HOOK_JOB_COMPLETED` variables are not set for the runner process, or the wrapper scripts are not executable.   |
| `node: command not found`, or the hook exits immediately           | Node.js is not installed on the runner or is not on `PATH` as `node`. On ARC, confirm the Node path used by your wrapper scripts matches your runner image.              |
| `No policy configured from Policy Store` when a policy is attached | The policy is attached to a scope that does not cover this job, or it is in the **Not attached** state. Check the attachment in the Policy Store.                        |
| Policy Store request fails with a non-200 status                   | The runner cannot reach `STEP_API`. Confirm egress from the runner to the StepSecurity API endpoint.                                                                     |
| Policy enforcement confirmation times out                          | The agent is not running or not healthy for this job. Check the agent log printed by the post-job hook, and on VM runners confirm the agent service started for the job. |
| No StepSecurity section in the GitHub job summary                  | Confirm your scenario publishes a summary, then check the post-job hook lines for the summary request result.                                                            |
| Runtime activity not attributed to the right job                   | Confirm the pre-job hook ran and reported a correlation ID for the job. Correlation is what ties agent telemetry to a specific run and job.                              |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.stepsecurity.io/github-actions/harden-runner/harden-runner-installation/runner-job-hooks.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
