# Windows

Two different questions get confused here, so this page separates them.

1. **Can a Windows machine host the station?** Yes, today, if it has Git for
   Windows.
2. **Can a step be written in PowerShell?** Yes, today, on any host.

A third - *can the station itself be pure PowerShell, with no bash at all* - is
designed and not built. It is at the bottom of this page.

## Hosting the loop on Windows

```powershell
.\station.ps1                 # preflight, then run the station
.\station.ps1 --check         # preflight only, change nothing
.\station.ps1 -- --once       # everything after -- goes to station.sh
```

**`station.ps1` is a launcher, not a port.** It finds the bash that Git for
Windows installed and hands over to `start.sh`. It re-implements nothing.

That is deliberate. The capture pattern has exactly one implementation, and a
PowerShell copy of it would be a second one that drifts - in the least visible
way possible, because a buffered port gives every line the same timestamp,
which reads like a working log while destroying the only property the log is
for.

Git for Windows ships bash 4.4+ with GNU coreutils. Git is the transport, so a
Windows host without git cannot participate anyway: the dependency is already
paid for.

### Finding bash, and the one that must not be found

Order matters: an explicit `HELIOGRAPH_BASH` override first, then the registry
(authoritative for where Git for Windows actually landed), then the usual
install paths, then `PATH`.

**`PATH` is last on purpose.** WSL puts a `bash.exe` in `System32` that is not
Git bash - it launches a Linux distribution, or fails with an install prompt if
none exists. A lookup finding that first would fail with a confusing WSL
message that says nothing about heliograph, so it is explicitly ignored.

```powershell
$env:HELIOGRAPH_BASH = 'D:\tools\Git\bin\bash.exe'   # if git is somewhere unusual
```

### Surviving a logout

[`service.ps1`](/service) registers a scheduled task. It does **not** look for
bash itself - it starts `station.ps1`, so the registry lookup stays in one
place.

## Steps written in PowerShell

A step is an argv array, so the runner does not care what language it is in. A
Windows question wants `Get-WinEvent`, not a bash reimplementation of it.

```bash
winev)  ps_step ./steps/win-events.ps1 ;;
```

`ps_step` exists so the four things that make PowerShell output unreadable in a
captured log are fixed **once**, rather than in every step by every author who
remembers. Every number below was measured on Windows Server 2022 with
PowerShell 5.1.20348 and on Linux with pwsh 7.6.5.

**Line endings.** `powershell.exe -File` emits CRLF - measured CR=8, LF=8 for
an eight-line script, whether redirected to a file or through a pipe. A stray
CR on every line is invisible in a terminal, wrong in the file, and quietly
breaks any later `grep` anchored with `$`. Rewriting each line with an explicit
LF drops CR to 0.

**Hyperlinks, not colour.** The capture already strips ANSI `ESC[` sequences,
which covers everything pwsh 7 emits for `Format-Table`: 8 ESC bytes in, 0 out.
The gap is OSC 8 hyperlinks - `ESC]8;;<url>` - which that pattern does not
match. Measured 4 ESC bytes surviving for a step calling
`$PSStyle.FormatHyperlink`, and 0 with `NO_COLOR` set. `NO_COLOR` is what earns
its place here.

**Encoding.** Forcing UTF-8 is about the OEM codepage mangling non-ASCII, not
about UTF-16: the redirected stream measured NUL=0, so the widely repeated
"PowerShell redirects as UTF-16" does not apply to this path.

**Exit codes, and this one would have shipped a wrong answer.** Running the
step in-process as `& './step.ps1'` leaves `$LASTEXITCODE` holding whatever the
last *native* command inside it returned. The shipped Windows snapshot ends by
probing `git config --get core.autocrlf`, which exits 1 when the key is unset -
so a perfectly good snapshot reported failure. Invoking the step as a child
with `-File` fixes it: measured 0 for a clean run, 3 for a genuine `exit 3`,
and 0 for a run whose internal native command exited 7.

`--mode` is answered before any interpreter is looked for, so a step's
declaration is readable on a machine with no PowerShell on it at all.

## Line endings, and what they really break

The station ships a `gitattributes` file that pins the transport repo to LF.

**The point is not to protect Windows from itself.** Git for Windows' bash
strips CR and runs a CRLF checkout perfectly well - measured on Server 2022
with Git 2.55. The point is that CRLF which gets **committed** breaks every
Linux clone afterwards, and breaks it silently when a file is *sourced*: on
Linux, `caplib.sh` reports `set: pipefail: invalid option name` and then
**carries on** with neither `-u` nor `pipefail` applied.

So `start.sh` probes the bash it is actually running under rather than
assuming, and reports one of three things: LF throughout, a CR-tolerant bash
with no `.gitattributes` (a warning - it runs here and would break a Linux
clone), or a CR-intolerant bash with CRLF files (a failure, with the fix).

If you get it wrong: `git config --global core.autocrlf false` and clone again.
The checkout is disposable and anything already pushed is safe.

## A pure PowerShell station

For a Windows Server estate with no Git for Windows and no permission to
install it. *"We support Windows, provided you first install Git for Windows"*
is a weak claim on exactly the estates this targets.

**Designed, specced, and not built.** The design settles on Windows PowerShell
5.1 as the floor - in-box on Server 2016 and later, so it needs no install -
carrying the git, file share and relay transports.

It is permitted only on one condition, which is the condition the whole
argument turns on: a second implementation of the capture is allowed **only
while it passes [the conformance suite](/conformance)**. An unproven port is
forbidden, because the failure mode is a log that reads perfectly and is
useless.

The design is in
[`docs/specs/2026-09-08-powershell-station-and-full-documentation-design.md`](https://github.com/dbhq-uk/heliograph/blob/main/docs/specs/2026-09-08-powershell-station-and-full-documentation-design.md).
This page will describe it when it exists, and not before.
