feat(evidence): implement getEvidenceJwks JWK Set endpoint (v0.1.25.32)#194
Merged
Conversation
Publication half of the v0.2 signer-key-resolution layer (design on #103 / aeoess#43; contract added to cycles-protocol-v0.yaml v0.1.25.6 in runcycles/cycles-protocol#113). - JwksController: public GET /v1/.well-known/cycles-jwks.json. Reads the shared cycles.evidence.signing.signer-did + new kid / nbf-ms via @value (no injected bean, so it loads in every @WebMvcTest without extra wiring). Cache-Control: public, max-age=300 — NOT immutable (a key set rotates). 404 via the standard NOT_FOUND ErrorResponse when no raw-hex key is configured. - JwksDocuments: pure builder. Raw 64-hex signer-did -> one active Ed25519 OKP JWK {kty,crv,alg,x=base64url(hex-decode(signer_did)),kid (default first-16-hex), cycles_nbf_ms (default 0), status:active}; cycles_exp_ms omitted = open-ended. The x is the SAME 32 bytes EnvelopeSigner signs with, so a verifier resolving the set authenticates the emitted signatures. did:cycles / blank / malformed -> empty (404): a did:cycles signer_did carries no key bytes (that + retired-key rotation history are the v0.2-store follow-up). - SecurityConfig: /v1/.well-known/** public (public keys only; private key never served; API-base-relative per the authority-scope rule). SecurityConfigTest updated for the new entry. - application.properties: cycles.evidence.signing.kid / nbf-ms. pom .31 -> .32. Tests: JwksDocumentsTest (10), JwksControllerTest (4, contract-validated against #113's spec). Both new classes 100% line-covered; full mvn verify 906 green; jacoco 95% gate met. codex review: no findings. No change to existing endpoints.
This was referenced Jun 15, 2026
amavashev
added a commit
that referenced
this pull request
Jun 15, 2026
JwksEndpointIntegrationTest (extends BaseIntegrationTest: full @SpringBootTest RANDOM_PORT, real Tomcat + the Spring Security filter chain ACTIVE, Testcontainers Redis) proves the JWK Set endpoint serves over real HTTP WITHOUT an API key — the /v1/.well-known/** public-path exemption actually holds end-to-end through the filter chain, not just as an array entry (the JwksControllerTest @WebMvcTest runs with filters disabled, so it can't show this). With the evidence signing identity set via @TestPropertySource: GET /v1/.well-known/cycles-jwks.json with no header → 200 + a JWK whose x decodes to exactly the configured signer_did bytes, correct kid/cycles_nbf_ms/status, and Cache-Control: public, max-age (NOT immutable); a bogus API key still yields 200 (public, never 401). The base class's contract-validating interceptor also checks the body against the published CyclesEvidenceJwks schema (cycles-protocol@main, #113). codex review: no findings. 2 tests; test-only (impl shipped in v0.1.25.32 / #194; no production/wire/spec change).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The reference implementation of the v0.2 signer-key-resolution publication endpoint —
getEvidenceJwks. This is PR 2 of 2; the spec-first half landed in runcycles/cycles-protocol#113 (cycles-protocol-v0.yamlv0.1.25.6). Together they let a consumer resolve adid:cyclessigner_did(or confirm a raw-hex one) to a public key and establish signer authority, not just signature validity.Design signed off by APS on aeoess#43; tracked in #103.
Changes (
v0.1.25.31 → .32)JwksController— publicGET /v1/.well-known/cycles-jwks.json. Reads the sharedcycles.evidence.signing.signer-did+ newkid/nbf-msvia@Value(holds no injected bean → loads in every@WebMvcTestwithout new wiring).Cache-Control: public, max-age=300— not immutable (a key set rotates). 404 via the standardNOT_FOUNDErrorResponsewhen no raw-hex key is configured.JwksDocuments— pure builder. Raw 64-hexsigner-did→ one active Ed25519 OKP JWK:{kty:OKP, crv:Ed25519, alg:EdDSA, x:base64url(hex-decode(signer_did)), kid (default = first 16 hex), cycles_nbf_ms (default 0), status:active};cycles_exp_msomitted ⇒ open-ended. Thexis the same 32 bytesEnvelopeSignersigns with, so a verifier resolving the set authenticates the emitted signatures.did:cycles/ blank / malformed → empty (404) — adid:cyclessigner_didcarries no key bytes.SecurityConfig—/v1/.well-known/**public (public keys only; private key never served; API-base-relative per the authority-scope rule).SecurityConfigTestupdated.application.properties(kid/nbf-ms), pom revision, AUDIT entry.Scope / deferred
v0.1 raw-hex publication. Retired-key rotation history (a key store with
cycles_exp_mswindows) anddid:cycles-form publication are the additive v0.2-store follow-up — and can't be built before the endpoint exists.Tests / verification
JwksDocumentsTest(10),JwksControllerTest(4 — 200 + JWK-set body + short public non-immutable cache,@WebMvcTestcontract-validated against fix(deps): bump Spring Boot 3.5.11→3.5.13, pin tomcat 10.1.54 (v0.1.25.16) #113's spec on main; unconfigured/did:cycles→NOT_FOUND).mvn verify906 tests green; jacoco 95% gate met.x↔EnvelopeSignerbyte parity,did:cycles404, no private-key reference, 200/404 contract shapes).Refs: #113, #103, aeoess#43.