Skip to content

Commit c057eef

Browse files
yaronfcursoragent
andcommitted
docs: ready JWX plan for v0.6.0 cutover and ML-DSA
Go 1.27 is out; target jwx v4-only on httpsign v0.6.0 and treat post-quantum ML-DSA via foreign JWS as an explicit goal. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent de382d3 commit c057eef

2 files changed

Lines changed: 86 additions & 42 deletions

File tree

internal-docs/JWX.md

Lines changed: 85 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,79 +4,120 @@ Single source of truth for optional jwx-backed JWS support in httpsign.
44

55
## Role in httpsign
66

7-
jwx is used only for **optional “foreign” JWS** signing and verification (`NewJWSSigner` / `NewJWSVerifier` on jwx v2, and `NewJWSSignerV3` / `NewJWSVerifierV3` on jwx v3). **Native** algorithms (HMAC-SHA256, RSA, RSASSA-PSS, P-256/P-384, Ed25519) do **not** use jwx.
7+
jwx is used only for **optional “foreign” JWS** signing and verification. **Native** algorithms (HMAC-SHA256, RSA, RSASSA-PSS, P-256/P-384, Ed25519) do **not** use jwx.
88

9-
Today `go.mod` pulls both `github.com/lestrrat-go/jwx/v2` and `.../jwx/v3`. `crypto.go` dispatches on the embedded foreign signer/verifier interfaces (v2 vs v3 parameter order differs).
9+
**Today (pre-cutover):** `go.mod` still pulls both `github.com/lestrrat-go/jwx/v2` and `.../jwx/v3`. Public API is `NewJWSSigner` / `NewJWSVerifier` (v2) and `NewJWSSignerV3` / `NewJWSVerifierV3` (v3). `crypto.go` dispatches on the embedded foreign signer/verifier interfaces (v2 vs v3 parameter order differs).
1010

11-
## Current policy (until Go 1.27)
11+
**Target:** a single jwx **v4** path, one constructor pair, and **post-quantum (PQ) signatures** as a first-class capability (see below).
1212

13-
- Stay on the dual v2+v3 paths.
14-
- Bump **within `jwx/v3`** (patch/minor) if needed for fixes.
15-
- Do **not** adopt jwx v4 on Go 1.24 / 1.26 + `GOEXPERIMENT=jsonv2`.
16-
- Do **not** deprecate only the v2 constructors (“use V3”) — that would send callers to V3 and then remove V3 in the next major.
13+
**Release vehicle:** ship as **httpsign `v0.6.0`** (breaking within `0.x`; not a leap to `v1.0.0`). Under Go modules, `v0.y.z` may introduce breaking API/dependency changes on a minor bump — that is intentional here (Go floor, drop jwx v2/v3, collapse constructors).
1714

18-
## Decision: all-in on jwx v4 after Go 1.27 stable
15+
---
16+
17+
## Explicit goals
18+
19+
1. **Drop dual jwx support** — cut over to **jwx v4 only** on **Go 1.27+**, released as **`v0.6.0`**. Details in the next sections.
20+
2. **PQ signatures** — make **ML-DSA (FIPS 204)** usable for RFC 9421 HTTP Message Signatures through httpsign’s foreign-JWS path (and document it), in **`v0.6.0`** or a fast follow (`v0.6.x` / `v0.7.0` only if PQ slips).
21+
22+
### PQ signatures (goal detail)
23+
24+
Go 1.27 adds stdlib [`crypto/mldsa`](https://pkg.go.dev/crypto/mldsa). jwx v4 registers **ML-DSA-44 / ML-DSA-65 / ML-DSA-87** natively when built with Go 1.27+ (`jwa.MLDSA44()`, `MLDSA65()`, `MLDSA87()`; `*mldsa.PrivateKey` / `*mldsa.PublicKey` work with `jws.Signer` / `jws.Verifier`). The companion [`github.com/jwx-go/mldsa/v4`](https://github.com/jwx-go/mldsa) is **not** required on our Go floor.
25+
26+
**In scope for httpsign**
27+
28+
- After the v4 cutover, `NewJWSSigner` / `NewJWSVerifier` must accept ML-DSA algs + `crypto/mldsa` keys the same way they accept classical JWS algs (no special `GOEXPERIMENT`, no optional build tags).
29+
- Round-trip tests: sign and verify an HTTP request (or signature base) with at least one parameter set (prefer **ML-DSA-65** as the default demo; cover 44/87 if cheap).
30+
- README / release notes: call out PQ via foreign JWS; note that RFC 9421 does not assign HTTP-sig algorithm identifiers for ML-DSA — callers use the JWS/`alg` story (or omit `alg` per profile) as with other foreign algorithms.
31+
- Reject `NoSignature` and other unsafe algs unchanged.
32+
33+
**Out of scope (for now)**
34+
35+
- **Native** `NewMLDSASigner`-style constructors that bypass jwx (revisit only if foreign-JWS overhead or API clarity demands it).
36+
- **Hybrid composite** signatures ([`jwx-go/compsig`](https://github.com/jwx-go/compsig), draft-ietf-jose-pq-composite-sigs) — track as a follow-on once the draft and companion stabilize.
37+
- **ML-KEM / JWE / HPKE** — encryption, not HTTP message signatures.
1938

20-
**Agreed (2026-07):** once **Go 1.27.0** (stable, not RC) is out, cut over httpsign to **`github.com/lestrrat-go/jwx/v4` only** (pin **v4.2.0+**) as an **httpsign major**.
39+
**Why fold PQ into this work:** the Go 1.27 + jwx v4 cutover is exactly what unlocks stdlib ML-DSA without experiments or extra modules. Shipping `v0.6.0` without exercising PQ would leave the main benefit of the floor unused.
2140

22-
### Why wait for 1.27
41+
---
42+
43+
## Status (2026-08-26): execute the cutover
44+
45+
The July 2026 gate is **met**. Do **`v0.6.0`** now; do **not** keep dual v2+v3 any longer than the cutover PR.
2346

24-
- **Go 1.26:** `encoding/json/v2` still needs `GOEXPERIMENT=jsonv2`; jwx v4 would force that on every httpsign build.
25-
- **Go 1.27:** `encoding/json/v2` / `jsontext` are stdlib packages; `encoding/json` is backed by v2. Opt-out is `GOEXPERIMENT=nojsonv2` (temporary). See [go1.27 notes](https://go.dev/doc/go1.27), [golang/go#71497](https://github.com/golang/go/issues/71497), [golang/go#76406](https://github.com/golang/go/issues/76406).
26-
- jwx **v4.2.0** (2026-07-24) is mature enough; the gate is the Go toolchain, not jwx.
47+
| Gate | Status |
48+
|------|--------|
49+
| Go **1.27.0** stable on [go.dev/dl](https://go.dev/dl/) | **Met** (released 2026-08-19) |
50+
| `encoding/json/v2` in stdlib (no `GOEXPERIMENT=jsonv2`) | **Met** — see [Go 1.27 notes](https://go.dev/doc/go1.27) |
51+
| jwx v4 mature | **Met** — use **`v4.4.0+`** (v4.2.0 was the original floor; current latest as of this update is v4.4.0) |
52+
| Smoke: `go get …/jwx/v4@v4.4.0` under `GOTOOLCHAIN=go1.27.0`, no `GOEXPERIMENT` | **Confirmed** locally (2026-08-26) |
53+
| Stdlib **`crypto/mldsa`** + jwx native ML-DSA (PQ goal) | **Met** on Go 1.27+ (no `jwx-go/mldsa` companion needed) |
2754

28-
### Trigger to implement
55+
### Why this was deferred (history)
2956

30-
1. Go **1.27.0** available on `go.dev/dl`.
31-
2. Confirm `go get github.com/lestrrat-go/jwx/v4@v4.2.0` (or newer) builds **without** `GOEXPERIMENT=jsonv2` under Go 1.27.
32-
3. Ship the cutover as an httpsign **major**.
57+
- On Go 1.26, jwx v4 required `GOEXPERIMENT=jsonv2` on every httpsign build — rejected for a library.
58+
- Dual v2+v3 was an interim; we deliberately avoided a “deprecate v2 → use V3” step that would bounce callers twice.
59+
- **Agreed (2026-07):** all-in on jwx v4 after Go 1.27 stable, as a **breaking** httpsign release.
60+
- **Agreed (2026-08-26):** Go 1.27 is out; **start the cutover** and ship it as **`v0.6.0`** (not `v1.0.0`).
61+
62+
---
63+
64+
## Decision: all-in on jwx v4 (`v0.6.0`)
65+
66+
Cut over to **`github.com/lestrrat-go/jwx/v4` only**, pin **≥ v4.4.0**, drop v2 and v3 in **`v0.6.0`**.
3367

3468
### What the cutover does
3569

70+
- Raise `go` / toolchain / CI to **Go 1.27.0+**.
3671
- Drop `jwx/v2` and `jwx/v3` from `go.mod`.
37-
- Single constructor pair on v4 types (preferred names: `NewJWSSigner` / `NewJWSVerifier`; remove `*V3`).
38-
- Update CI / `go` directive to **1.27.0+**.
39-
- Follow upstream [MIGRATION.md](https://github.com/lestrrat-go/jwx/blob/v4.2.0/MIGRATION.md) and optionally [jwxmigrate](https://github.com/jwx-go/jwxmigrate).
40-
- Re-run foreign-JWS tests; note v4 behavioral tightenings in [Changes-v4.md](https://github.com/lestrrat-go/jwx/blob/v4.2.0/Changes-v4.md).
72+
- Single constructor pair on v4 types: **`NewJWSSigner` / `NewJWSVerifier`** (remove `*V3`; retire the v2-typed overloads).
73+
- Follow upstream [MIGRATION.md](https://github.com/lestrrat-go/jwx/blob/v4.4.0/MIGRATION.md) and optionally [jwxmigrate](https://github.com/jwx-go/jwxmigrate) (`jwxmigrate/v4`).
74+
- Re-run foreign-JWS tests (classical + **ML-DSA**); note behavioral tightenings in [Changes-v4.md](https://github.com/lestrrat-go/jwx/blob/v4.4.0/Changes-v4.md) if any affect jwa/jws-only use.
75+
- Update `CLAUDE.md` / README snippets that still recommend dual v2/v3 or `*V3`; document PQ via foreign JWS.
76+
- Tag and release **`v0.6.0`**.
4177

4278
### Deprecation / messaging
4379

44-
- **No** mid-stream v2→V3-only deprecation.
45-
- Either remove v2 and `*V3` together in the major with a short migration note, **or** add `// Deprecated:` on **both** only when announcing that major (pointing at the new v4-backed constructors) — never “prefer V3” as a stable end state.
80+
- **No** mid-stream “prefer V3” deprecation before `v0.6.0`.
81+
- In `v0.6.0`: remove v2 constructors and `*V3` together; release notes document the caller steps below.
82+
- Optional: if a short transition branch is needed for review only, both old APIs may carry `// Deprecated:` pointing at the v4-backed `NewJWS*` — never ship “V3 is the stable end state.”
4683

47-
### Customer impact (v3 → v4)
84+
### Customer impact
4885

49-
Typical callers of `NewJWSSignerV3` / `NewJWSVerifierV3` only pass a `jwa` algorithm, a key, and httpsign config/fields. Expected change:
86+
| Caller | Change |
87+
|--------|--------|
88+
| **Native algorithms only** | Go **1.27+** floor; otherwise unaffected |
89+
| **`NewJWSSignerV3` / `NewJWSVerifierV3`** | Bump to **`v0.6.0`**; import `jwx/v3/jwa``jwx/v4/jwa`; rename to `NewJWSSigner` / `NewJWSVerifier` (v4 `jwa` uses function forms like `jwa.ES256()`) |
90+
| **`NewJWSSigner` / `NewJWSVerifier` (v2)** | Same release: switch to v4 imports + v4 algorithm values; no separate `*V3` step |
5091

51-
1. Go **1.27+**
52-
2. httpsign **major**
53-
3. Import `jwx/v3/jwa``jwx/v4/jwa`
54-
4. Rename `NewJWS*V3``NewJWS*` (if we collapse names)
55-
56-
No change to key types or httpsign signing/verify config. **Native-algorithm users** are unaffected aside from the Go version floor. Exotic jwx features (ES256K companions, custom jwx signers, JWKS fetch) are out of scope for most httpsign users.
92+
No change to key types or httpsign `SignConfig` / `VerifyConfig` / `Fields` for classical callers. **PQ callers** bring `crypto/mldsa` keys and `jwa.MLDSA*()` through the same `NewJWS*` constructors. ES256K / Ed448 companions, custom jwx signers, and JWKS fetch remain niche; composite PQ is a later follow-on (see Explicit goals).
5793

5894
### Toolchain after cutover
5995

6096
| Item | Requirement |
6197
|------|-------------|
62-
| Go | **1.27.0+** in `go.mod` / CI |
63-
| `GOEXPERIMENT=jsonv2` | Not required on Go 1.27+ |
98+
| Go | **1.27.0+** in `go.mod` / CI (today CI is still 1.24) |
99+
| `GOEXPERIMENT=jsonv2` | Not required; do not set |
64100
| `GOEXPERIMENT=nojsonv2` | Avoid in CI |
65101

102+
Upstream still allows Go 1.26 + `GOEXPERIMENT=jsonv2` for jwx v4; **httpsign will not support that** — floor is 1.27 so consumers never need the experiment.
103+
66104
---
67105

68-
## Implementation checklist (when Go 1.27 is out)
106+
## Implementation checklist (do now)
69107

70-
Scoped to httpsign’s use of **jwa** + **jws** only (no JWT/JWE/JWK fetch in library glue). Full upstream detail: [MIGRATION.md](https://github.com/lestrrat-go/jwx/blob/v4.2.0/MIGRATION.md).
108+
Scoped to httpsign’s use of **jwa** + **jws** only (no JWT/JWE/JWK fetch in library glue). Upstream detail: [MIGRATION.md (v4.4.0)](https://github.com/lestrrat-go/jwx/blob/v4.4.0/MIGRATION.md).
71109

72110
### Code / deps
73111

74-
- [ ] `go.mod`: Go 1.27.0+; require `github.com/lestrrat-go/jwx/v4` (≥ v4.2.0); remove v2 and v3.
75-
- [ ] Rewrite imports `jwx/v2|v3``jwx/v4`; collapse constructors; update `sign()` / `verify()` dispatch for v4 `jws.Signer` / `jws.Verifier` (ex-`Signer2` / `Verifier2`).
76-
- [ ] Confirm factory APIs (`SignerFor` / `VerifierFor` or v4 equivalents) and `NoSignature` rejection still work.
77-
- [ ] Run `jwxmigrate` if helpful; fix remaining compile/test failures by hand.
78-
- [ ] CI: Go 1.27; do not set `jsonv2` / `nojsonv2` experiments.
79-
- [ ] Release notes: httpsign major; document caller steps above; link upstream Changes-v4 if relevant.
112+
- [ ] `go.mod`: Go 1.27.0+; require `github.com/lestrrat-go/jwx/v4` (**≥ v4.4.0**); remove v2 and v3.
113+
- [ ] Rewrite imports `jwx/v2|v3``jwx/v4`; collapse constructors; update `sign()` / `verify()` dispatch for v4 `jws.Signer` / `jws.Verifier` (renamed from v3 `Signer2` / `Verifier2`; parameter order matches today’s V3 path: key before payload).
114+
- [ ] Confirm factory APIs (`SignerFor` / `VerifierFor`) and `NoSignature` rejection still work.
115+
- [ ] Drop v2↔v3 cross-compat tests; keep round-trip tests on the single v4 path.
116+
- [ ] **PQ:** foreign-JWS round-trip with `crypto/mldsa` + `jwa.MLDSA65()` (and smoke 44/87 if cheap); document in README/release notes.
117+
- [ ] Run `jwxmigrate --fix` if helpful; fix remaining compile/test failures by hand.
118+
- [ ] CI (`test.yml`, `lint.yml`, CodeQL): Go **1.27**; do not set `jsonv2` / `nojsonv2`.
119+
- [ ] Docs: README / `CLAUDE.md` / this file — remove dual-version guidance; **`v0.6.0`** release notes with caller steps + PQ; link upstream Changes-v4 if relevant.
120+
- [ ] Tag **`v0.6.0`** and publish.
80121

81122
### Upstream items likely N/A or low priority
82123

@@ -85,5 +126,8 @@ Scoped to httpsign’s use of **jwa** + **jws** only (no JWT/JWE/JWK fetch in li
85126
| JWK `Import` generics, `ParseFS`, custom field registration | Unlikely / N/A |
86127
| HTTP JWKS → `jwkfetch` | N/A in core; document if examples ever use `jku` |
87128
| ES256K / Ed448 / asmbase64 companions | Only if we expose or test those algs |
129+
| `jwx-go/mldsa/v4` companion | **N/A on Go 1.27+** (native in jwx); do not add the module |
130+
| Composite PQ (`jwx-go/compsig`) | Follow-on after pure ML-DSA; draft-dependent |
131+
| ML-KEM / HPKE | N/A (not signatures) |
88132
| Custom `Signer`/`Verifier` with `Algorithm()` | We embed upstream implementations only — re-check types after rename |
89133
| Detached payload `io.Reader` | Orthogonal (we sign the RFC 9421 base string as `[]byte`) |

internal-docs/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This directory contains internal documentation for maintainers of the httpsign l
44

55
## Contents
66

7-
- **JWX.md** — Optional jwx / foreign-JWS support: current dual v2/v3 state, and the plan to move to **jwx v4.2.0+** after **Go 1.27 stable** (httpsign major).
7+
- **JWX.md** — Optional jwx / foreign-JWS: cut over to jwx v4.4.0+ on Go 1.27+ as **httpsign `v0.6.0`**, with **ML-DSA PQ signatures** as an explicit goal. Gate met 2026-08-26.
88

99
## Purpose
1010

0 commit comments

Comments
 (0)