@@ -26,8 +26,8 @@ cross builds must run the smoke on the target in CI.
2626
2727` <httpserver/features.hpp> ` exposes the same compiled ` query_features() ` declaration
2828in both builds, without backend headers or configuration macros. Availability of
29- the provider is distinct from availability of TCP TLS, HTTP/2, and HTTP/3. TCP TLS is available in TLS-on builds for certificate-authenticated HTTP/1.0
30- and HTTP/1.1 in both managed and external modes. HTTP/2 and HTTP/3 remain
29+ the provider is distinct from availability of TCP TLS, HTTP/2, and HTTP/3. TCP TLS is available in TLS-on builds for certificate, initial mTLS and eligible
30+ external-PSK HTTP/1.0 and HTTP/1.1 in both managed and external modes. HTTP/2 and HTTP/3 remain
3131unavailable until their owning tasks implement public listeners. ` listen() `
3232rejects unsupported protocol/profile requests before binding;
3333` server_options::validate() ` remains semantic.
@@ -58,7 +58,7 @@ checks configure/build provenance, installed metadata, effective public headers,
5858recursive dependency graphs and all static-archive members, including unused
5959members. Provider build proof remains distinct from encrypted listener or QUIC
6060interoperability proof. Nonlocal BSD, macOS and Windows execution belongs to CI
61- and the v3 PR; public H2/H3 listeners and expanded TLS profiles remain later-task work.
61+ and the v3 PR; public H2/H3 listeners remain later-task work.
6262
6363Run the clean installed-consumer matrix from a bootstrapped source checkout:
6464
@@ -163,12 +163,10 @@ requests before binding. Every endpoint binds before admission activates; failur
163163on a later bind synchronously releases earlier endpoints and reports no bound
164164ports. A bind failure is terminal for that server instance.
165165
166- TASK-193 supports public certificate/default/SNI configuration and replacement
167- lifetime safety. Public mTLS, external PSK and ACME operations remain TASK-194
168- work; native requests for unsupported profiles reject honestly. Private profile
169- suites continue to test their existing support. The rotation example now uses the
170- public store and demonstrates typed publication outcomes; expanded installed
171- profile/rotation examples belong to TASK-194.
166+ TASK-193 supplies public certificate/default/SNI configuration and generation
167+ lifetime safety. TASK-194 extends the same store to initial mTLS, HTTP/1 external
168+ PSK and ACME, and exercises live listener rotation through installed packages.
169+ Private profile suites remain supporting protocol evidence.
172170
173171Run the installed certificate capability matrix after staging a package:
174172
@@ -186,3 +184,70 @@ Unknown/absent SNI checks verify the chain and compare the selected certificate
186184with its independently hostname-verified named certificate. TLS-off runs the same
187185configuration code and requires typed unavailability. Nonlocal platform checks
188186remain CI/v3-PR owned.
187+
188+ ## Public initial client authentication, external PSK and ACME
189+
190+ ` server/tls_credentials.hpp ` supplies provider-neutral host trust, authentication,
191+ owned keys and a bounded callback runtime. Set ` tls_host_credentials::profile `
192+ and the listener's ` options.tls().profile ` consistently. For mTLS, set
193+ ` trust_roots_pem ` to the client CA roots. ` mutual_tls ` defaults to ` require ` ;
194+ explicit ` client_auth.mode = tls_client_certificate_mode::request ` verifies any
195+ presented chain while allowing anonymous peers. ` require ` refuses absent or
196+ untrusted client chains. Set the same resolved mode on the listener. Authentication
197+ is initial-handshake only. Exchanges and request hooks own immutable verified
198+ leaf identity, fingerprint and validity metadata.
199+
200+ A bound store rejects publication that contradicts any active listener's profile,
201+ resolved mode or enabled HTTP/1 policy. Bind registration and publication share
202+ one private writer transaction; a concurrent writer cannot downgrade authentication
203+ between validation and admission. Compatible listeners hold independent leases;
204+ stopping admission releases that listener's lease. Already accepted sessions keep
205+ their pinned generation. Invalid material and policy leave the active generation
206+ unchanged. Mixed profiles in one store cannot attach to a listener configured for
207+ one contradictory profile.
208+
209+ For HTTP/1 external PSK, create ` tls_psk_runtime ` with finite ` lookup_workers `
210+ (1–64) and ` lookup_queue ` (0–65536), supply ` tls_psk_config::lookup ` and its shared
211+ runtime, and set the host/listener profile to ` external_psk ` . Certificate/key/trust
212+ material must be empty for PSK. Return ` tls_psk_result ` with an accepted status and
213+ move-only ` tls_psk_key ` built from owned bytes. Identity/context references last
214+ one invocation; retain copies when needed. TLS1.3 identities are binary; TLS1.2
215+ exposes textual NUL-terminated identities. The context owns canonical selected
216+ host, generation identity, absolute monotonic deadline and cancellation token.
217+ Callbacks can run concurrently and must synchronize mutable captured state.
218+
219+ The lookup runtime is distinct from native server-owned provider workers. It
220+ counts active and queued callbacks together; ignored cancellation retains a slot
221+ until callback return, including across rotation. Late keys are wiped and never
222+ route requests. ` request_stop() ` is nonblocking. Call it before
223+ ` wait_retired(absolute_deadline) ` to observe actual callback retirement: timeout
224+ means application work remains; waiting from that runtime's callback returns
225+ ` would_deadlock ` . Destruction requests stop without joining arbitrary application
226+ code. Captured resources must be owned through retirement. A native handler's
227+ ` request_stop() ` or ` begin_drain() ` does not stop a shared application runtime;
228+ one server stopping cannot disable another server using that runtime.
229+
230+ Normal certificate sessions have a host/context namespace. Same-generation
231+ resumption may reuse authentication; replacements create fresh contexts, so an
232+ old session cannot bypass the replacement or cross host/store identity. External
233+ PSK authentication differs from ticket resumption: PSK profiles and ACME disable
234+ tickets/session caching. Every ACME handshake sends its challenge certificate.
235+ Application 0-RTT remains disabled; early requests never enter routes.
236+
237+ ` tls_credentials::publish_acme(tls_acme_challenge) ` validates a supplied challenge
238+ certificate/key, canonical DNS host, 32-byte authorization digest and future
239+ absolute system-clock expiry no later than certificate expiry. The certificate
240+ must have exactly one matching DNS SAN and one critical ACME authorization digest
241+ extension. ` remove_acme(host) ` is canonical and idempotent. Publication validation
242+ is independent of listener port. Selection requires matching exact canonical SNI,
243+ sole ` acme-tls/1 ` , TCP, and the socket's actual local port443. Other ALPN offers and
244+ ordinary HTTP use normal credentials; challenge sessions cannot route HTTP. This
245+ API performs no certificate issuance or challenge-client orchestration.
246+
247+ TLS-off installs identical declarations and secure key operations, with typed
248+ ` not_supported ` for credential/runtime/ACME availability. Public H2/H3 listeners
249+ remain later-task work; external PSK remains HTTP/1-only. The installed rotation
250+ example takes ` certificate-directory managed|external port ` , opens a real public
251+ listener and accepts ` replace ` , ` invalid ` and ` stop ` on stdin. Its independent
252+ installed witness holds a usable connection across rotation and validates the
253+ new certificate plus rejected-publication preservation.
0 commit comments