Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,27 @@

All notable changes to Rail are documented here.

## Unreleased

### Tests
- **Ed25519 pinned to RFC 8032 published vectors** (#48): sign + verify
+ tamper-rejection known-answer tests; suite 178 → 182.
- **TLS stack pinned to published vectors** (#49): RFC 8448 §3 full
offline handshake (key schedule + transcript + server/client Finished),
ECDSA-P256 verify per RFC 6979 §A.2.5 plus a 4-way invalid-signature
rejection test, the CertificateVerify path (ASN.1 parse → SPKI extract
→ DER sig parse → ECDSA), and SAN hostname validation against a real
captured leaf (accept exact + case-fold, reject other host and parent
domain); suite 182 → 187. The handshake, cert-verify, and hostname
paths — the code that decides who to trust — were previously covered
only by manual tests in `tools/tls/`.
- **Suite harness: per-test `arena_mark`/`arena_reset`** (#49). Adding
the TLS tests exposed that `run_test` kept every compile's garbage
live across the whole run: by test ~183 the default 1GB arena was
full and the GC crawled — a test that compiles in seconds standalone
spun for 20+ minutes in-suite. Each test now compiles inside a
mark/reset window, so suite memory stays flat at any test count.

## v5.3.0 — 2026-07-08 — Independence + 3× faster self-compile

Consolidation release: 19 PRs since v5.2.0. Two headlines — a
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</p>

<p align="center">
<a href="#quick-start"><img src="https://img.shields.io/badge/tests-182%2F182-brightgreen" alt="tests 182/182"></a>
<a href="#quick-start"><img src="https://img.shields.io/badge/tests-187%2F187-brightgreen" alt="tests 187/187"></a>
<a href="#why-rail"><img src="https://img.shields.io/badge/self--hosting-fixed%20point-blue" alt="self-hosting"></a>
<a href="#what-rail-does"><img src="https://img.shields.io/badge/HTTPS-pure%20Rail-ff5500" alt="pure-Rail HTTPS"></a>
<a href="#how-it-works"><img src="https://img.shields.io/badge/GC-ARM64%20assembly-purple" alt="GC in ARM64 asm"></a>
Expand All @@ -34,7 +34,7 @@ Rail compiles itself. The compiler — ~9,200 lines of Rail — produces a ~0.9
```
./rail_native self && cp /tmp/rail_self ./rail_native # cycle 1
./rail_native self && cmp rail_native /tmp/rail_self # cycle 2 — byte-identical
./rail_native test # 182/182
./rail_native test # 187/187
```

## Quick start
Expand All @@ -50,7 +50,7 @@ Apple Silicon (ARM64 macOS) is the primary target; Linux ARM64, Linux x86_64, We
```bash
./rail_native <file.rail> # compile to /tmp/rail_out
./rail_native run <file.rail> # compile + execute
./rail_native test # run the 182-test suite
./rail_native test # run the 187-test suite
./rail_native self # self-compile, fixed point at gen2
./rail_native x86 <file.rail> # cross-compile to Linux x86_64
./rail_native linux <file.rail> # cross-compile to Linux ARM64
Expand Down
6 changes: 3 additions & 3 deletions docs/STATUS.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# Rail - verified status (v5.3.0)

> Generated by `tools/deploy/gen_status.rail` on 2026-07-08 19:37 UTC.
> Generated by `tools/deploy/gen_status.rail` on 2026-07-09 01:06 UTC.
> Every number is recomputed from the source tree on each run, so this file
> cannot drift. Where a claim needs proof, the **command** is given - run it
> yourself. These are reproducible provenance facts, not a proof of correctness.

| Fact | Value | Verify it yourself |
|---|---|---|
| Release | v5.3.0 | `git describe --tags` |
| Compiler | 9232 lines of Rail | `wc -l tools/compile.rail` |
| Compiler | 9245 lines of Rail | `wc -l tools/compile.rail` |
| Seed binary | 908360 bytes | `wc -c rail_native` |
| Seed sha256 | `44e28d4780658866d82201aa6cbc6efcc94704e191ac70a9587e19f240eaf35c` | `shasum -a 256 rail_native` |
| Seed sha256 | `ba9344da55733580a04c9593e9062fded821c86829fa326219de5a39297889f2` | `shasum -a 256 rail_native` |
| Stdlib | 102 modules | `ls stdlib/*.rail \| wc -l` |
| Non-Rail surface | 31 in-tree files | `SHIMS.md` |
| Tests | runner self-reports N/N | `./rail_native test` |
Expand Down
Binary file modified rail_native
Binary file not shown.
Binary file modified rail_safe
Binary file not shown.
2 changes: 1 addition & 1 deletion rail_safe.sha256
Original file line number Diff line number Diff line change
@@ -1 +1 @@
44e28d4780658866d82201aa6cbc6efcc94704e191ac70a9587e19f240eaf35c
ba9344da55733580a04c9593e9062fded821c86829fa326219de5a39297889f2
19 changes: 16 additions & 3 deletions tools/compile.rail
Original file line number Diff line number Diff line change
Expand Up @@ -5852,6 +5852,12 @@ strip_trailing_ws s = strip_trailing_ws_loop s (length s)

run_test name src expected =
let _ = print (cat [" --- ", name, " ---"])
-- Mark/reset per test: each compile_program allocates ~MBs that nothing
-- references after the verdict. Without the reset the arena fills over
-- ~180 in-process compiles and the GC crawls (a late test that compiles
-- in seconds standalone spins for 20+ minutes in-suite at the default
-- 1GB arena). result is an unboxed bool, so nothing escapes the window.
let mk = arena_mark 0
let asm = compile_program src
let path = trim (shell "mktemp -u /tmp/rail_out_XXXXXX")
let _ = build path asm
Expand All @@ -5860,6 +5866,7 @@ run_test name src expected =
-- Get the last line (exit code) and everything before it (stdout)
let result = get_test_result lines expected
let _ = shell (cat ["rm -f ", path, " ", path, ".s ", path, ".o"])
let _ = arena_reset mk
result

get_test_result lines expected =
Expand Down Expand Up @@ -6332,9 +6339,15 @@ run_tests _ =
let t176 = run_test "ed25519_reject_tampered" "import \"stdlib/sha256.rail\"\nimport \"stdlib/sha512.rail\"\nimport \"stdlib/x25519.rail\"\nimport \"stdlib/ed25519.rail\"\nmain =\n let pub = hex_to_bytes \"d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a\"\n let msg = arr_new 0 0\n let sig_r = hex_to_bytes \"e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b\"\n let _ = arr_set sig_r 0 (bit_and (arr_get sig_r 0 + 1) 255)\n let sig_s = hex_to_bytes \"e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b\"\n let _ = arr_set sig_s 32 (bit_and (arr_get sig_s 32 + 1) 255)\n let _ = print (cat [show (ed25519_verify pub msg 0 sig_r), \" \", show (ed25519_verify pub msg 0 sig_s)])\n 0" "0 0"
let t177 = run_test "ed25519_sign_rfc8032_t3" "import \"stdlib/sha256.rail\"\nimport \"stdlib/sha512.rail\"\nimport \"stdlib/x25519.rail\"\nimport \"stdlib/ed25519.rail\"\nimport \"stdlib/ed25519_scalar.rail\"\nimport \"stdlib/ed25519_sign.rail\"\nmain =\n let sk = hex_to_bytes \"c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7\"\n let msg = hex_to_bytes \"af82\"\n let pk = ed25519_pk_from_sk sk\n let sig = ed25519_sign sk msg 2\n let _ = print (cat [bytes_to_hex pk 32, \":\", bytes_to_hex sig 64])\n 0" "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025:6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a"
let s_ed = (if t174 then 1 else 0) + (if t175 then 1 else 0) + (if t176 then 1 else 0) + (if t177 then 1 else 0)
let total = s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10 + s11 + s12 + s_inf + s_bx + s_ed
let _ = print (cat [show total, "/182 tests passed"])
if total == 182 then 0 else 1
let t178 = run_test "tls13_rfc8448_handshake" "import \"stdlib/tls13_client.rail\"\nmain =\n let ch = hex_to_bytes \"010000c00303cb34ecb1e78163ba1c38c6dacb196a6dffa21a8d9912ec18a2ef6283024dece7000006130113031302010000910000000b0009000006736572766572ff01000100000a00140012001d0017001800190100010101020103010400230000003300260024001d002099381de560e4bd43d23d8e435a7dbafeb3c06e51c13cae4d5413691e529aaf2c002b0003020304000d0020001e040305030603020308040805080604010501060102010402050206020202002d00020101001c00024001\"\n let sh = hex_to_bytes \"020000560303a6af06a4121860dc5e6e60249cd34c95930c8ac5cb1434dac155772ed3e2692800130100002e00330024001d0020c9828876112095fe66762bdbf7c672e156d6cc253b833df1dd69b1b04e751f0f002b00020304\"\n let sf = hex_to_bytes \"080000240022000a00140012001d00170018001901000101010201030104001c00024001000000000b0001b9000001b50001b0308201ac30820115a003020102020102300d06092a864886f70d01010b0500300e310c300a06035504031303727361301e170d3136303733303031323335395a170d3236303733303031323335395a300e310c300a0603550403130372736130819f300d06092a864886f70d010101050003818d0030818902818100b4bb498f8279303d980836399b36c6988c0c68de55e1bdb826d3901a2461eafd2de49a91d015abbc9a95137ace6c1af19eaa6af98c7ced43120998e187a80ee0ccb0524b1b018c3e0b63264d449a6d38e22a5fda430846748030530ef0461c8ca9d9efbfae8ea6d1d03e2bd193eff0ab9a8002c47428a6d35a8d88d79f7f1e3f0203010001a31a301830090603551d1304023000300b0603551d0f0404030205a0300d06092a864886f70d01010b05000381810085aad2a0e5b9276b908c65f73a7267170618a54c5f8a7b337d2df7a594365417f2eae8f8a58c8f8172f9319cf36b7fd6c55b80f21a03015156726096fd335e5e67f2dbf102702e608ccae6bec1fc63a42a99be5c3eb7107c3c54e9b9eb2bd5203b1c3b84e0a8b2f759409ba3eac9d91d402dcc0cc8f8961229ac9187b42b4de100000f000084080400805a747c5d88fa9bd2e55ab085a61015b7211f824cd484145ab3ff52f1fda8477b0b7abc90db78e2d33a5c141a078653fa6bef780c5ea248eeaaa785c4f394cab6d30bbe8d4859ee511f602957b15411ac027671459e46445c9ea58c181e818e95b8c3fb0bf3278409d3be152a3da5043e063dda65cdf5aea20d53dfacd42f74f3140000209b9b141d906337fbd2cbdce71df4deda4ab42c309572cb7fffee5454b78f0718\"\n let cp = hex_to_bytes \"49af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea5005\"\n let r = tls13_client_handshake_offline cp ch 196 sh 90 sf 657 0 \"\"\n let _ = print (cat [show (arr_get r 4), \":\", bytes_to_hex (arr_get r 0) 32, \":\", bytes_to_hex (arr_get r 1) 32, \":\", bytes_to_hex (arr_get r 2) (arr_get r 3)])\n 0" "1:9e40646ce79a7f9dc05af8889bce6552875afa0b06df0087f792ebb7c17504a5:a11af9f05531f856ad47116b45a950328204b4f44bfb6b3a4b4f1f3fcb631643:14000020a8ec436d677634ae525ac1fcebe11a039ec17694fac6e98527b642f2edd5ce61"
let t179 = run_test "ecdsa_p256_rfc6979_verify" "import \"stdlib/sha256.rail\"\nimport \"stdlib/ecdsa_p256.rail\"\nmain =\n let qx = hex_to_bytes \"60fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6\"\n let qy = hex_to_bytes \"7903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299\"\n let h = hex_to_bytes \"af2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e9891562113d8a62add1bf\"\n let r = hex_to_bytes \"efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716\"\n let s = hex_to_bytes \"f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8\"\n let _ = print (show (ecdsa_p256_verify qx qy h r s))\n 0" "1"
let t180 = run_test "ecdsa_p256_reject_invalid" "import \"stdlib/sha256.rail\"\nimport \"stdlib/ecdsa_p256.rail\"\nmain =\n let qx = hex_to_bytes \"60fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6\"\n let qy = hex_to_bytes \"7903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299\"\n let h = hex_to_bytes \"af2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e9891562113d8a62add1bf\"\n let r_bad = hex_to_bytes \"efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3717\"\n let s = hex_to_bytes \"f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8\"\n let h_bad = hex_to_bytes \"af2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e9891562113d8a62add1be\"\n let r_good = hex_to_bytes \"efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716\"\n let z = arr_new 32 0\n let e1 = ecdsa_p256_verify qx qy h r_bad s\n let e2 = ecdsa_p256_verify qx qy h_bad r_good s\n let e3 = ecdsa_p256_verify qx qy h z s\n let e4 = ecdsa_p256_verify qx qy h r_good z\n let _ = print (cat [show e1, \" \", show e2, \" \", show e3, \" \", show e4])\n 0" "0 0 0 0"
let t181 = run_test "tls13_cert_verify_sig" "import \"stdlib/tls13_client.rail\"\nmain =\n let cert = hex_to_bytes \"3082011b3081c1a003020102020101300a06082a8648ce3d04030230173115301306035504030c0c746573742e6578616d706c65301e170d3236303431373031353031395a170d3237303431383031353031395a30173115301306035504030c0c746573742e6578616d706c653059301306072a8648ce3d020106082a8648ce3d03010703420004a2a0b43696b4451b5bdc9a3ae9ba68000f66f317586f19342da4987ca638f19e5b786809e58a7f6bd33c09da3c39e9e176c85286962434b79a8611646e8cd023300a06082a8648ce3d0403020349003046022100f0af1d0eaa7f47a57e80609f979014487bf39b2d06f770a7e9c30704b14f60e7022100b5fa084c52d995ac2f755f216a9bd98e85ff95840ad72f2a7e61478e8760048b\"\n let sig = hex_to_bytes \"304502203a241f6c75320a14bca59d59696f1b0ec80d0f40557a96a28b95aa863f1ccda00221008508b92136ac6b53b8147b4f21a5150318f9fd61a65edf658d780cee72ddd051\"\n let ts = hex_to_bytes \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n let _ = print (show (tls13_cert_verify_sig cert 287 1027 sig 71 ts))\n 0" "1"
let t182 = run_test "cert_hostname_san" "import \"stdlib/tls13_client.rail\"\nmain =\n let cert = hex_to_bytes \"3082039f30820346a00302010202105a2eb9c74ccea8470e184bd70d6c085f300a06082a8648ce3d040302303b310b3009060355040613025553311e301c060355040a1315476f6f676c65205472757374205365727669636573310c300a06035504031303574531301e170d3236303332383137313733335a170d3236303632363138313733305a301c311a3018060355040313116170692e616e7468726f7069632e636f6d3059301306072a8648ce3d020106082a8648ce3d03010703420004f15b46f29627170a03dfe671cacca9d84c200924577cbe89e362f0a1dd516d2a93a0b495332444fe5b9ed75cd62dfaa9ce3d95d4913213a9893369a4df41be0fa382024930820245300e0603551d0f0101ff04040302078030130603551d25040c300a06082b06010505070301300c0603551d130101ff04023000301d0603551d0e04160414ad1df0b855983a5719e9d6bde97c72425dec55c3301f0603551d230418301680149077923567c4ffa8cca9e67bd980797bcc93f938305e06082b0601050507010104523050302706082b06010505073001861b687474703a2f2f6f2e706b692e676f6f672f732f7765312f576934302506082b060105050730028619687474703a2f2f692e706b692e676f6f672f7765312e637274301c0603551d110415301382116170692e616e7468726f7069632e636f6d30130603551d20040c300a3008060667810c01020130360603551d1f042f302d302ba029a0278625687474703a2f2f632e706b692e676f6f672f7765312f33474c426c74304c3436512e63726c30820103060a2b06010401d6790204020481f40481f100ef007500499c9b69de1d7cecfc36decd8764a6b85baf0a878019d15552fbe9eb29ddf8c30000019d35aa6ec4000004030046304402203182860d5c3baf671e5870f04e4b5a5c8a784950f7cd8c9e9e82866e19383ca502202a303ec43bac6b52a92bc79655f1c40be9d73f163518026a6592c090076af6f50076000e5794bcf3aea93e331b2c9907b3f790df9bc23d713225dd21a925ac61c54e210000019d35aa6ace0000040300473045022045ed6976393b807764edd9b0f55e94258a7ece08b8083d811122bd1ae2974c52022100d6fb2755bbda522e57b12faa64cabe64589c57f4df90547a125fa18cfb43666f300a06082a8648ce3d040302034700304402205cd2321541e590fc30d18eab25b4784355866f5bebed4ad30b5b66699914dbdd022078ebc05c00314a35b145dbe7dc783db9094204b089cc03c876b6ade7c9b9e68b\"\n let r1 = cv_validate_hostname cert 931 \"api.anthropic.com\"\n let r2 = cv_validate_hostname cert 931 \"evil.example.com\"\n let r3 = cv_validate_hostname cert 931 \"API.ANTHROPIC.COM\"\n let r4 = cv_validate_hostname cert 931 \"anthropic.com\"\n let _ = print (cat [show r1, \" \", show r2, \" \", show r3, \" \", show r4])\n 0" "1 0 1 0"
let s_tls = (if t178 then 1 else 0) + (if t179 then 1 else 0) + (if t180 then 1 else 0) + (if t181 then 1 else 0) + (if t182 then 1 else 0)
let total = s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10 + s11 + s12 + s_inf + s_bx + s_ed + s_tls
let _ = print (cat [show total, "/187 tests passed"])
if total == 187 then 0 else 1

-- ===========================================================================
-- M6 — self-hosted emit path. Replaces as/ld/codesign with the pure-Rail
Expand Down
Loading