Skip to content

perf(#6262): eliminate net/http and crypto from WASM build - #6264

Closed
fullsend-ai-coder[bot] wants to merge 1 commit into
mainfrom
agent/6262-wasm-size-reduction
Closed

perf(#6262): eliminate net/http and crypto from WASM build#6264
fullsend-ai-coder[bot] wants to merge 1 commit into
mainfrom
agent/6262-wasm-size-reduction

Conversation

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

Summary

  • Eliminate net/http, crypto/tls, crypto/rsa, crypto/x509, and math/big from the mintcore WASM dependency tree. These packages contributed ~1.1 MB gzip to the binary via net/http's transitive crypto/tls dependency. The WASM binary drops from ~2.0 MB to ~1.3 MB gzip (4.7 MB raw), well within the CF Workers free-tier 3 MiB limit and providing ample headroom for PR refactor(#812): unify mintcore handler construction via getEnv injection #6255.

  • New Doer interface replaces HTTPDoer for transport-agnostic outbound HTTP. Non-WASM uses HTTPClientDoer wrapping http.Client; WASM uses HostFetchDoer calling JS fetch — no *http.Request/*http.Response types needed.

  • RSA crypto offloaded to host Web Crypto on WASM. Build-tagged implementations: Go crypto/rsa on non-WASM (!js), HostCryptoSigner/HostCryptoVerifier on WASM (js). JWT signing delegates to crypto.subtle.sign, JWKS verification delegates to crypto.subtle.verify.

  • Handler.HandleRaw provides a net/http-free entry point used by cmd/mint-wasm; ServeHTTP wraps it on non-WASM platforms.

  • Makefile wasm-build now uses -ldflags "-s -w" and hard-fails when gzip exceeds 3 MiB (was: warn at 3 MB, fail only at 10 MB paid-tier limit).

Size measurements

Build Raw Gzip
Before (main, no -s -w) 7.3 MB 2.0 MB
After (this PR, -s -w) 4.7 MB 1.3 MB
Savings 2.6 MB 0.7 MB
CF Workers free-tier limit 3.0 MB

Breaking change: cmd/mint-wasm init signature

mintcoreInitMint now requires 5 arguments (was 3):

mintcoreInitMint(configJSON, fetchCallback, pemCallback, cryptoSignCallback, cryptoVerifyCallback)

The CF Worker adapter (workersrc/src/index.ts) must be updated to pass the two new crypto callbacks that wrap crypto.subtle.sign and crypto.subtle.verify.

Test plan

  • go test ./internal/mintcore/... -race — all tests pass
  • go test ./internal/dispatch/gcf/ -run TestEmbeddedMintSource — embed sync passes
  • go build ./... — full repo builds
  • go vet ./internal/mintcore/... — no issues
  • WASM build: GOOS=js GOARCH=wasm go build -ldflags "-s -w" succeeds, 1.28 MB gzip
  • WASM dep check: go list -deps confirms no net/http, crypto/rsa, crypto/tls, math/big
  • CF Worker adapter update (follow-up: pass crypto callbacks in index.ts)
  • Behaviour CI CF preview deploy (validates end-to-end with Wrangler)

🤖 Generated with Claude Code


Closes #6262

Post-script verification

  • Branch is not main/master (agent/6262-wasm-size-reduction)
  • Secret scan passed (gitleaks — dc87d7d6484555ba91661a3e94f126287a27246f..HEAD)
  • PR body secret scan passed (gitleaks — no-git)

Remove net/http, crypto/tls, crypto/rsa, crypto/x509, and math/big
from the mintcore WASM dependency tree. These packages contributed
~1.1 MB gzip to the binary via net/http's transitive crypto/tls
dependency. The WASM binary drops from ~2.0 MB to ~1.3 MB gzip,
well within the CF Workers free-tier 3 MiB limit and providing
ample headroom for PR #6255 (mintcore handler unification).

Architecture:
- New Doer interface replaces HTTPDoer for transport-agnostic
  outbound HTTP (no net/http types). Non-WASM uses HTTPClientDoer
  wrapping http.Client; WASM uses HostFetchDoer calling JS fetch.
- RSA operations (JWT signing, JWKS signature verification) are
  build-tagged: Go crypto on non-WASM (!js), host Web Crypto
  callbacks on WASM (js). HostCryptoSigner and HostCryptoVerifier
  parallel the existing HostFetchDoer/HostPEMAccessor pattern.
- Handler.HandleRaw provides a net/http-free entry point used by
  cmd/mint-wasm; ServeHTTP wraps it on non-WASM via handler_http.go.
- JWKSVerifier on WASM caches raw JWK entries (not *rsa.PublicKey)
  and delegates signature verification to the host.
- Shared JWT parsing and claim validation extracted to jwt_parse.go.
- Makefile wasm-build now uses -ldflags "-s -w" and hard-fails when
  gzip exceeds 3 MiB (was: warn at 3 MB, fail at 10 MB).
- cmd/mint-wasm initMint now takes 5 args (added cryptoSign and
  cryptoVerify callbacks); Worker JS adapter must be updated.

Before: 7.3 MB raw / 2.0 MB gzip (net/http + crypto/tls linked)
After:  4.7 MB raw / 1.3 MB gzip (no net/http, crypto, or math/big)

Closes #6262
@fullsend-ai-coder
fullsend-ai-coder Bot requested a review from a team as a code owner August 16, 2026 18:36
@fullsend-ai-coder fullsend-ai-coder Bot added the ready-for-review Agent PR ready for human review label Aug 16, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 16, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 6:37 PM UTC · Completed 6:56 PM UTC

Commit: ee02e32 · View workflow run →

@fullsend-ai-review

Copy link
Copy Markdown

Review

Findings

High

  • [breaking-change-marker] PR title — PR title is perf(#6262): eliminate net/http and crypto from WASM build but the PR body acknowledges a breaking change to mintcoreInitMint (5 args, was 3). Per COMMITS.md, breaking changes must carry the ! suffix: perf(#6262)!: eliminate net/http and crypto from WASM build.
    Remediation: Add the ! suffix to the PR title.

  • [breaking-api-change] cmd/mint-wasm/main.go:50mintcoreInitMint changed from 3 arguments to 5. The CF Worker adapter (workersrc/src/index.ts) must be updated atomically — the old adapter will fail to initialize with the new WASM binary. The PR does not include the adapter update.
    Remediation: Coordinate the CF Worker JS adapter update for simultaneous deployment, or document the deployment ordering in the PR body.

  • [stale-doc] docs/contributing/go-code.md:20 — Documents gcfSkip as having "three current entries" (fetch_js.go, pem_js.go, file_pem.go). This PR adds crypto_js.go, github_crypto_js.go, and jwks_verifier_js.go, making it six entries. A developer consulting this doc would miss the new entries.
    Remediation: Update to list all six gcfSkip entries.

Medium

  • [response-size-limit-bypass] internal/mintcore/fetch_js.goHostFetchDoer.Do() has no response body size limit. HTTPClientDoer (non-WASM) enforces 1 MiB via io.LimitReader. The maxJWKSResponseLen constant (512 KB) in jwt_parse.go is now dead code — defined but never referenced. A large upstream response could exhaust Worker memory.
    Remediation: Add a response body size check in HostFetchDoer.Do(), or enforce the limit in the JWKS verifier after receiving the body. Remove or reference the dead maxJWKSResponseLen constant.

  • [stale-doc] docs/contributing/go-code.md:11 — The CF Worker adapter paragraph describes the bridge as handling "Fetch Request/Response mapping." The WASM bridge now uses transport-agnostic types (HandleRaw with plain strings/maps) and mintcoreInitMint now also requires crypto callbacks (cryptoSignCallback, cryptoVerifyCallback).
    Remediation: Update to mention crypto callbacks and transport-agnostic types.

  • [trust-boundary] internal/mintcore/crypto_js.goHostCryptoSigner.SignRS256() passes the full PEM private key data as a string to the JS host on every sign call. The existing HostPEMAccessor already crosses this trust boundary, so this is incremental rather than a new class of risk. Consider having the host cache the imported CryptoKey rather than receiving raw PEM on every call.

Low

  • [missing-size-limit] cmd/mint-wasm/main.go:166 — The WASM handleFetch path passes the request body to HandleRaw without size validation. The non-WASM ServeHTTP enforces a 64 KB limit. CF Workers impose their own limits (larger than 64 KB), partially mitigating this.

  • [nil-deref] internal/mintcore/jwks_verifier_js.go — WASM NewJWKSVerifier does not nil-check opts.HTTPClient, unlike the non-WASM variant which falls back to NewHTTPClientDoer. The only caller always passes fetchDoer, so this is a latent defect rather than an active crash path.

  • [unnecessary-complexity] internal/mintcore/fetch_js.go:46marshalStringMap and escapeJSONString are custom JSON serializers whose comments claim to avoid importing encoding/json. However, encoding/json is already imported by shared files (handler.go, github.go, jwt_parse.go, jwks_verifier_js.go) that compile for the js build — the package is in the WASM dependency tree regardless. The misleading comment and unnecessary custom implementation add maintenance burden.

  • [scope-exceeded] internal/mintcore/handler.go — Several inline doc-comments explaining authorization design rationale were removed (dual enrollment, foreign grant fallback, 404-vs-transient error handling, userMsg disclosure control). The authorization logic is unchanged, but the comments documented security-sensitive design decisions that aid future maintainers.

  • [header-case-sensitivity] internal/mintcore/handler.goHandleRaw checks both "Authorization" and "authorization" but not other mixed-case variants. This covers the two real-world cases (HTTP/1.1 canonical and HTTP/2 lowercase via Fetch API).


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR

@fullsend-ai-review fullsend-ai-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the review comment for full details.

Comment thread cmd/mint-wasm/main.go
if len(args) < 3 {
return "mintcoreInitMint requires 3 arguments: configJSON, fetchCallback, pemCallback"
if len(args) < 5 {
return "mintcoreInitMint requires 5 arguments: configJSON, fetchCallback, pemCallback, cryptoSignCallback, cryptoVerifyCallback"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[high] breaking-api-change

mintcoreInitMint changed from 3 arguments to 5. The CF Worker adapter (workersrc/src/index.ts) must be updated atomically — the old adapter will fail to initialize with the new WASM binary. The PR does not include the adapter update.

Suggested fix: Coordinate the CF Worker JS adapter update for simultaneous deployment, or document the deployment ordering in the PR body.

Comment thread cmd/mint-wasm/main.go
ctx := context.Background()
status, respHeaders, respBody := handler.HandleRaw(ctx, method, path, headers, []byte(body))

// Build response headers JSON.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] missing-size-limit

The WASM handleFetch path passes the request body to HandleRaw without size validation. The non-WASM ServeHTTP enforces a 64 KB limit. CF Workers impose their own limits, partially mitigating this.

}

// Call the JS fetch callback synchronously via Await.
// Call the JS fetch callback synchronously via awaitPromise.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] unnecessary-complexity

marshalStringMap and escapeJSONString are custom JSON serializers whose comments claim to avoid importing encoding/json. However, encoding/json is already imported by shared files that compile for the js build — the package is in the WASM dependency tree regardless.

@ifireball

Copy link
Copy Markdown
Member

Superseded: the #6262 regression was fixed on PR #6255 via VerifierFactory (OIDC audience resolved in NewHandler; verifiers take a plain string). net/http/crypto elimination is not needed at this time.

@fullsend-ai-retro

fullsend-ai-retro Bot commented Aug 16, 2026

Copy link
Copy Markdown

🤖 Finished Retro · ✅ Success · Started 9:41 PM UTC · Completed 9:56 PM UTC

Commit: ee02e32 · View workflow run →

@fullsend-ai-retro

Copy link
Copy Markdown

Retro: PR #6264perf(#6262): eliminate net/http and crypto from WASM build

What happened

PR #6264 was a 35-file, +2064/−1280 line refactor by the code agent to eliminate net/http and crypto from the mintcore WASM build, responding to issue #6262 (WASM binary exceeds CF Workers 3 MiB limit). The PR was closed without merging because the underlying regression was already being fixed on PR #6255 via a surgical VerifierFactory pattern change.

Root cause of wasted work: Issue #6262 was filed at 17:56 UTC with a detailed but incorrect root-cause analysis. The issue attributed the WASM size bloat to inherent costs of crypto/* and net/http in the Go WASM toolchain. In reality, the bloat was caused by a linker pattern bug in PR #6255's commit 39c01b89 (15:26 UTC), which passed getEnv closures into verifier constructors, pulling additional dependency subtrees into the WASM binary. The code agent faithfully implemented the issue's prescribed approaches (Web Crypto offload + slim HTTP types) — the problem was that the diagnosis was wrong, not that the agent's execution was poor.

Timeline:

  1. 11:51 — PR refactor(#812): unify mintcore handler construction via getEnv injection #6255 created (getEnv injection refactor for issue Unify mintcore handler construction across entrypoints #812)
  2. 15:26 — PR refactor(#812): unify mintcore handler construction via getEnv injection #6255 commit introduces the regression
  3. 17:56 — Issue mintcore: WASM artifact exceeds CF Workers free-tier limit (blocks CF mint / behaviour CI) #6262 filed with incorrect root-cause analysis
  4. 18:01 — Triage echoes issue's recommendations, labels ready-to-code
  5. 18:01 — Code agent starts (35 min)
  6. 18:36 — PR perf(#6262): eliminate net/http and crypto from WASM build #6264 opened
  7. 18:56 — Review agent posts CHANGES_REQUESTED (12 findings)
  8. 18:56 — Fix agent fails in ~8s (bot-detection bug)
  9. 19:39 — Human discovers real root cause, pushes VerifierFactory fix to PR refactor(#812): unify mintcore handler construction via getEnv injection #6255
  10. 21:39 — Human closes PR perf(#6262): eliminate net/http and crypto from WASM build #6264 as superseded

Total agent runs on this PR lifecycle: 19 runs across triage (2), code (2, 1 failed), review (8, 2 cancelled, 2 in-progress at close), fix (5, 2 failed), retro (2).

What went well

  • Review agent quality was strong. The review correctly identified 12 findings including the breaking API change (mintcoreInitMint 3→5 args without updating the CF Worker adapter), missing response body size limits on the WASM path, unnecessary custom JSON serializers, and removed security documentation. These were all real issues in the code as written.
  • Code agent execution was faithful. The agent implemented exactly what the issue asked for: approaches A (Web Crypto offload) and B (slim HTTP types). The WASM binary was reduced from 2.0 MB to 1.3 MB gzip. The implementation was architecturally sound.
  • Human review caught the fundamental issue. The human reviewer (ifireball) recognized the entire PR was unnecessary once the VerifierFactory fix landed, and closed it promptly.

Evidence for existing tracked issues

Proposals filed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-review Agent PR ready for human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

mintcore: WASM artifact exceeds CF Workers free-tier limit (blocks CF mint / behaviour CI)

1 participant