Reproduction steps:
- Windows, Git Bash, station-v0.67. Module manifest is newline-free (13188 bytes, 0 LF, 0 CR).
- Run: railcall market module sign .
- Check the manifest: python -c "b=open('module.json','rb').read(); print(len(b), b.count(b'\n'), b.count(b'\r'))"
Expected: signing does not modify module.json.
Actual: 19330 bytes, 610 LF, 610 CR. The manifest was rewritten with indent=2 and CRLF line endings.
Source — _market_module_sign() in railcall_cli.py (~line 5153) rewrites the manifest before signing:
with open(os.path.join(module_dir, "module.json"), "w", encoding="utf-8") as f:
json.dump(manifest, f, indent=2, ensure_ascii=False)
f.write("\n")
Text mode on Windows converts every \n to \r\n.
Why this passes every existing check: .gitattributes marks module.json -text, so the CRLF commits verbatim and checks out unchanged. Bytes match, signature verifies, CI is green on 3.10/3.12/3.13. Nothing surfaces the corruption.
Inferred impact (read from source, NOT tested — flagging the distinction): on install the station writes the manifest from a wire string in text mode, railcall_cli.py ~line 4201:
with open(os.path.join(module_dir, "module.json"), "w", encoding="utf-8") as f:
f.write(payload_bundle["module_json"])
If that reasoning holds, then on a Windows buyer's machine an LF manifest arrives as CRLF and the signature breaks; a CRLF manifest arrives as \r\r\n and breaks worse. A newline-free manifest survives both. That matches why linear-guard made its manifest newline-free at v1.5.4 — the workaround was found empirically without the cause being identified.
I have not verified the install-side breakage on a second Windows machine, so please treat that half as a hypothesis. The manifest rewrite itself is directly observed.
Secondary: the same function writes module.sig in text mode, producing a 130-byte CRLF signature file instead of 129-byte LF. Harmless (outside the signed tree, read with .strip()) but inconsistent.
Note on timing: my CLI updated 2026-08-10 00:35 and the rewrite appeared ~40 minutes later, so this may be a recent regression rather than long-standing.
Workaround in use: a local signer that emits the identical payload and never touches module.json.
Station version ('railcall version'): station-v0.67
Module slug + version: muhammad-akif-janjua/linear-guard v1.5.6