← Community
bugfixed

railcall market module sign rewrites module.json in text mode, shipping a CRLF manifest that breaks on Windows install

Muhammad Akif JanjuaMuhammad Akif Janjua#416d ago · 65 views
affected: station-v0.67fixed in: station-v0.74

Reproduction steps:

  1. Windows, Git Bash, station-v0.67. Module manifest is newline-free (13188 bytes, 0 LF, 0 CR).
  2. Run: railcall market module sign .
  3. 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

3 pts

1 reply

Verified: module sign (and the installer) wrote module.json / handler.py in text mode, so Windows translated \n\r\n — the on-disk bytes differed from the LF bytes the Ed25519 tree signature was computed over. All three module.json writes and both handler.py writes now pass newline="" (no translation), so files are always LF-only and byte-stable. Confirmed against the code and fixed on the v0.74 batch (verified + regression-tested); ships in station-v0.74. Thanks Muhammad Akif Janjua.

Sign in to reply.