agnara-http 0.1.0a8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. agnara_http-0.1.0a8/.gitignore +41 -0
  2. agnara_http-0.1.0a8/LICENSE +202 -0
  3. agnara_http-0.1.0a8/PKG-INFO +266 -0
  4. agnara_http-0.1.0a8/README.md +247 -0
  5. agnara_http-0.1.0a8/pyproject.toml +28 -0
  6. agnara_http-0.1.0a8/src/agnara_http/__init__.py +37 -0
  7. agnara_http-0.1.0a8/src/agnara_http/_asgi.py +71 -0
  8. agnara_http-0.1.0a8/src/agnara_http/_binding.py +780 -0
  9. agnara_http-0.1.0a8/src/agnara_http/_discovery.py +360 -0
  10. agnara_http-0.1.0a8/src/agnara_http/_dispatch.py +338 -0
  11. agnara_http-0.1.0a8/src/agnara_http/_documentation.py +550 -0
  12. agnara_http-0.1.0a8/src/agnara_http/_explorer.py +597 -0
  13. agnara_http-0.1.0a8/src/agnara_http/_exposures.py +85 -0
  14. agnara_http-0.1.0a8/src/agnara_http/_lifespan.py +134 -0
  15. agnara_http-0.1.0a8/src/agnara_http/_openapi.py +322 -0
  16. agnara_http-0.1.0a8/src/agnara_http/_problem.py +313 -0
  17. agnara_http-0.1.0a8/src/agnara_http/_publication.py +268 -0
  18. agnara_http-0.1.0a8/src/agnara_http/_redoc.py +188 -0
  19. agnara_http-0.1.0a8/src/agnara_http/_response.py +125 -0
  20. agnara_http-0.1.0a8/src/agnara_http/_routing.py +284 -0
  21. agnara_http-0.1.0a8/src/agnara_http/_scalar.py +186 -0
  22. agnara_http-0.1.0a8/src/agnara_http/_surfaces.py +259 -0
  23. agnara_http-0.1.0a8/src/agnara_http/_swagger.py +235 -0
  24. agnara_http-0.1.0a8/src/agnara_http/_vendor/redoc/2.5.3/LICENSE +22 -0
  25. agnara_http-0.1.0a8/src/agnara_http/_vendor/redoc/2.5.3/manifest.json +28 -0
  26. agnara_http-0.1.0a8/src/agnara_http/_vendor/redoc/2.5.3/redoc.standalone.js +1838 -0
  27. agnara_http-0.1.0a8/src/agnara_http/_vendor/redoc/2.5.3/redoc.standalone.js.LICENSE.txt +102 -0
  28. agnara_http-0.1.0a8/src/agnara_http/_vendor/scalar/1.67.0/LICENSE +21 -0
  29. agnara_http-0.1.0a8/src/agnara_http/_vendor/scalar/1.67.0/manifest.json +37 -0
  30. agnara_http-0.1.0a8/src/agnara_http/_vendor/scalar/1.67.0/standalone.js +2455 -0
  31. agnara_http-0.1.0a8/src/agnara_http/_vendor/swagger_ui/5.32.14/LICENSE +202 -0
  32. agnara_http-0.1.0a8/src/agnara_http/_vendor/swagger_ui/5.32.14/NOTICE +2 -0
  33. agnara_http-0.1.0a8/src/agnara_http/_vendor/swagger_ui/5.32.14/manifest.json +33 -0
  34. agnara_http-0.1.0a8/src/agnara_http/_vendor/swagger_ui/5.32.14/swagger-ui-bundle.js +2 -0
  35. agnara_http-0.1.0a8/src/agnara_http/_vendor/swagger_ui/5.32.14/swagger-ui-bundle.js.LICENSE.txt +104 -0
  36. agnara_http-0.1.0a8/src/agnara_http/_vendor/swagger_ui/5.32.14/swagger-ui.css +3 -0
  37. agnara_http-0.1.0a8/src/agnara_http/composition.py +960 -0
  38. agnara_http-0.1.0a8/src/agnara_http/py.typed +0 -0
@@ -0,0 +1,41 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+
9
+ # Environments
10
+ .venv/
11
+ venv/
12
+
13
+ # Tooling caches
14
+ .pytest_cache/
15
+ .pytest-*/
16
+ .ruff_cache/
17
+ .ty_cache/
18
+ .mypy_cache/
19
+ .coverage
20
+ coverage.xml
21
+ htmlcov/
22
+ test-results/
23
+
24
+ # uv
25
+ uv.lock.bak
26
+ .uv-cache/
27
+
28
+ # Locally generated release and validation artifacts. These directories
29
+ # hold built wheels and sdists; several are created by tooling that drops
30
+ # its own `*` .gitignore inside them, which only protects the repository
31
+ # for as long as that file survives. Name them here as well, so a
32
+ # hand-recreated directory cannot commit distributions.
33
+ .artifacts/
34
+ .dist-test/
35
+ .release-validation/
36
+
37
+ # Editors / OS
38
+ .idea/
39
+ .vscode/
40
+ .DS_Store
41
+ Thumbs.db
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright [yyyy] [name of copyright owner]
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
@@ -0,0 +1,266 @@
1
+ Metadata-Version: 2.5
2
+ Name: agnara-http
3
+ Version: 0.1.0a8
4
+ Summary: HTTP/ASGI exposure adapter for Agnara capabilities.
5
+ Project-URL: Homepage, https://github.com/Blandskron/agnara
6
+ Project-URL: Repository, https://github.com/Blandskron/agnara
7
+ Author-email: Agnara Maintainers <maintainers@agnara.dev>
8
+ License: Apache-2.0
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: Apache Software License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
16
+ Requires-Python: >=3.14
17
+ Requires-Dist: agnara==0.1.0a8
18
+ Description-Content-Type: text/markdown
19
+
20
+ # agnara-http
21
+
22
+ HTTP/ASGI exposure adapter. Owns routing, request decoding, response encoding, RFC 9457 mapping, OpenAPI generation and the authorized discovery endpoint.
23
+
24
+ ## Status
25
+
26
+ `0.1.0a4` exposes the seven-name public composition API described in
27
+ `docs/HTTP_COMPOSITION.md`, including path, query, header, JSON, cookie, form
28
+ and upload bindings. Documentation providers, Explorer and the discovery
29
+ endpoint are implemented internally but are not yet reachable from that public
30
+ composition surface.
31
+
32
+ This distribution is built and versioned with the synchronized workspace set.
33
+ Which versions exist on an index is answered by its PyPI project page, not by
34
+ this file: a README ships inside the artifact and cannot describe the state of
35
+ a publication that happens after it is built.
36
+
37
+ OpenAPI 3.2 is projected from compiled HTTP exposures and shared capability
38
+ schemas. Optional browser documentation providers consume that generated
39
+ contract; Swagger UI, ReDoc, Scalar or any other UI must remain replaceable
40
+ and must not become a dependency of `agnara-core`.
41
+
42
+ Agnara Explorer is not an OpenAPI renderer. If it is initially served through
43
+ this adapter, it consumes the filtered protocol-neutral introspection snapshot
44
+ defined by the core/application composition boundary.
45
+
46
+ ## How a request is served
47
+
48
+ Everything reflective happens once, at `Http.compile()`. Route templates are
49
+ parsed and checked for collisions, capabilities are resolved, execution plans
50
+ are compiled and every binding is validated against its plan's inputs. The
51
+ result is frozen, so it needs no lock and can be shared across the workers of
52
+ one process.
53
+
54
+ A request then costs a trie lookup, a binding pass over already-classified
55
+ sources, one core invocation and one serialization:
56
+
57
+ - an ASGI 3 single-callable boundary that accepts `http` scopes, accepts
58
+ `lifespan` only when a lifecycle is configured, and refuses any other
59
+ protocol rather than mistaking it for a supported one;
60
+ - a per-method route trie that prefers static segments, preserves significant
61
+ trailing slashes and reports allowed methods in registration order, so a
62
+ mismatch is a `405` carrying `Allow` rather than a `404`;
63
+ - compiled request binding with strict percent and UTF-8 decoding,
64
+ case-insensitive header lookup, documented scalar conversion and bounded
65
+ body reads. It produces an invocation payload for the shared core validation
66
+ path and never substitutes for capability schema validation;
67
+ - RFC 9457 `application/problem+json` for every failure, from one reviewed
68
+ status table.
69
+
70
+ The media type is settled from the headers before a single body byte is read,
71
+ and a route declares one body reading — JSON, form fields or uploads — because
72
+ one request has one body.
73
+
74
+ E6.4 adds deterministic internal success-response serialization. Successful
75
+ values are projected recursively to compact UTF-8 JSON and emitted as one ASGI
76
+ response-start event followed by one terminal body event. `None` produces a
77
+ bodyless `204`; `HEAD` preserves the equivalent representation headers while
78
+ suppressing transmitted body bytes. The complete value is checked before the
79
+ response starts, including cycles, finite numbers and string-only object keys.
80
+ Canonical failures are serialized by the separate RFC 9457 boundary below.
81
+
82
+ E6.5 adds internal RFC 9457 failure mapping. Every `FailureCode` is projected
83
+ through one explicit, exhaustive table to a reviewed HTTP status and an
84
+ occurrence-independent problem `title`, and emitted as
85
+ `application/problem+json` with the same deterministic compact UTF-8 JSON
86
+ encoding used for success. The stable machine-readable discriminator is the
87
+ `code` extension member, so applications that publish no problem
88
+ documentation keep the RFC 9457 default `about:blank` type; an application may
89
+ instead compile an explicit absolute base URI into one type URI per code.
90
+ Failure details are nested under a single `details` member so they cannot
91
+ shadow a reserved member, `internal_failure` never serializes handler message
92
+ or details, and a prebuilt last-resort internal problem response is available
93
+ to a dispatcher that cannot serialize an outcome. `WWW-Authenticate`,
94
+ `Retry-After`, content negotiation, `problem+xml` and multi-error arrays are
95
+ documented gaps rather than conformance claims; see ADR 0028.
96
+
97
+ E6.6a adds transport-level problems: the failures that happen before a
98
+ capability runs. Binding failures now carry a reason rather than only a
99
+ message, so a dispatcher selects a status from a contract instead of matching
100
+ text: malformed data becomes `400`, an unacceptable media type `415`, an
101
+ oversized body `413`, and a client disconnect becomes no response at all. A
102
+ missing route becomes `404` and a method mismatch `405` with the `Allow`
103
+ header RFC 9110 requires, attached during serialization so it cannot be
104
+ dropped at emission. Capability and transport failures share one problem-type
105
+ namespace keyed by the `code` extension, because `code` is what a client
106
+ reads. `401` and `429` stay absent until authentication and rate limiting
107
+ exist; see ADR 0030.
108
+
109
+ E6.6b is the request path itself. A declared exposure carries a method, a
110
+ path template, an `ExecutionPlan`, its input bindings and a body limit;
111
+ compilation validates all of it against the capability's real input schemas
112
+ and freezes an immutable registry, so a matched route resolves to its plan and
113
+ binding in one lookup and every declaration error fails at startup. Dispatch
114
+ then matches, binds, invokes and serializes with no reflection and no lock.
115
+ `HEAD` falls back to a `GET` exposure and suppresses only body bytes;
116
+ `root_path` is stripped so an application can be mounted; a client disconnect
117
+ produces no response; and a serialization failure falls back to the prebuilt
118
+ internal problem, which is the case that constant exists for. The problem
119
+ `instance` carries the path but never the query string, so a secret passed in
120
+ a query cannot be copied into a problem body. Every invocation runs as the
121
+ anonymous principal, which is why no path here produces a `401`; see ADR 0031.
122
+
123
+ E6.7 adds a dependency-free internal OpenAPI 3.2.0 projection from that same
124
+ compiled exposure registry. An exposure is absent by default and contributes
125
+ paths, identifiers, descriptions, tags and schemas only through explicit
126
+ publication metadata; filtering happens before document assembly. Parameters
127
+ and JSON request bodies reuse the capability plan's compiled input schemas.
128
+ The current response projection truthfully remains generic because the runtime
129
+ does not yet compile output schemas: `200` carries an unconstrained JSON value,
130
+ `204` carries no value, and a default RFC 9457 response references one shared
131
+ problem component. Compact sorted-key UTF-8 serialization is byte-stable for
132
+ identical compiled input. This does not add a schema route, CLI export, UI,
133
+ viewer-specific authorization or a complete conformance claim; see ADR 0032.
134
+
135
+ E6.12 adds the documentation-provider contract. ADR 0018 and RFC 0003 decided
136
+ that browser documentation sits behind an optional, replaceable boundary; this
137
+ is that boundary in code, so its guarantees are enforced rather than trusted.
138
+ A provider is given an already-filtered document or its local URL and nothing
139
+ that could reveal more: no route registry, no compiled exposure, no execution
140
+ plan, no capability. It must name the OpenAPI versions it was tested against
141
+ and the features it does not support, because a compatibility claim made by
142
+ silence is the one this project refuses. Asked for a version it does not
143
+ support, it becomes unavailable with a diagnostic instead of rendering
144
+ documentation that is wrong. Pinned local assets stay the baseline and an
145
+ empty registry is the supported no-UI deployment. E6.19 replaces the original
146
+ coarse remote-assets gate with exact resource declarations and an exact-origin
147
+ deployment allowlist; see ADRs 0033 and 0040.
148
+
149
+ E6.13 adds one internal compiled route layer for already-produced HTTP
150
+ surfaces. A schema, documentation page or future Explorer shell supplies a
151
+ stable logical name, an explicit static path, its media type, complete bytes
152
+ and optional safe headers; the layer does not know how any artifact was
153
+ generated. Compilation sorts declarations, rejects duplicate names and paths,
154
+ and reserves each surface path against every capability method so shadowing
155
+ and `405` behavior cannot depend on dispatcher order. At runtime `GET` serves
156
+ the immutable response, `HEAD` preserves its headers without body bytes,
157
+ other methods receive `405` with `Allow: GET, HEAD`, and unmatched exchanges
158
+ delegate unchanged to capability dispatch. No default route or public
159
+ configuration syntax is selected here, omission is not authorization, and UI
160
+ assets remain separate work; see ADR 0034.
161
+
162
+ E6.14 adds an immutable internal publication plan without a global boolean
163
+ bag. Schema, each documentation UI and Explorer are selected by supplying
164
+ their own typed configuration; absence means disabled, and no environment
165
+ silently changes that choice. Each UI owns its own try-it state, which defaults
166
+ off. A selected UI receives the configured schema URL only when that endpoint
167
+ is actually present; otherwise it receives the already-filtered serialized
168
+ document directly. Exactly one source is required. Explorer alone has no
169
+ OpenAPI dependency, an unused OpenAPI artifact publishes nothing, and all
170
+ selected paths pass through the E6.13 collision boundary before any provider
171
+ renders. The plan remains internal and does not implement provider HTML, CSP,
172
+ assets, visibility or authorization; see ADR 0035.
173
+
174
+ E6.15 adds internal local and CDN Swagger UI providers pinned to 5.32.14. The
175
+ local production baseline serves the verified Apache-2.0 bundle and stylesheet
176
+ from the application origin; runtime size and SHA-256 checks protect the
177
+ vendored bytes. The separately named CDN variant uses exact `unpkg.com` URLs
178
+ and SRI and remains unavailable until remote assets are explicitly permitted.
179
+ Both modes use a local initializer rather than inline JavaScript, disable the
180
+ online validator, URL configuration, credential persistence and credentialed
181
+ fetches, and leave try-it off unless this UI explicitly enables it. The
182
+ provider truthfully lists the OpenAPI 3.2 features deferred upstream; it claims
183
+ basic 3.2.0 rendering, not complete conformance. Provider composition and the
184
+ final emitted CSP remain internal follow-up work; see ADR 0036.
185
+
186
+ E6.16 adds internal local and CDN ReDoc CE providers pinned to 2.5.3. The
187
+ local variant serves a size/hash-verified MIT-licensed standalone bundle; the
188
+ CDN variant uses the byte-identical exact-version `cdn.redoc.ly` resource with
189
+ SRI and remains behind remote-asset permission. Both generate a same-origin
190
+ initializer, enable untrusted-spec sanitization, hide the download button and
191
+ declare ReDoc's runtime inline-style and blob-worker needs without enabling
192
+ inline JavaScript. ReDoc CE has no try-it console and upstream still does not
193
+ claim OpenAPI 3.2 support, so those requests become unavailable explicitly;
194
+ the canonical document is never downgraded. See ADR 0037.
195
+
196
+ E6.17 adds internal local and CDN Scalar 1.67.0 providers. E6.18 then drives
197
+ all three pinned local providers through a test-only HTTP bridge over the
198
+ compiled ASGI surface dispatcher and the exact emitted security headers.
199
+ Playwright 1.62.0 Chromium verifies rendering, CSP-blocked undeclared origins,
200
+ inert XSS payloads, disabled routes, non-persisted credential state,
201
+ same-origin unpublished OAuth redirect behavior, per-UI try-it, keyboard
202
+ entry and a 390 by 844 responsive smoke viewport. Playwright remains a
203
+ workspace development dependency, not an `agnara-http` dependency. These are
204
+ browser smoke tests rather than complete WCAG/OpenAPI conformance, so the
205
+ documentation default remains explicitly deferred; see ADRs 0038 and 0039.
206
+
207
+ E6.19 enforces the asset policy at the provider-independent registry boundary.
208
+ Each remote script or stylesheet must have an exact-version HTTPS URL, valid
209
+ SHA-384 SRI and anonymous CORS declaration; its rendered HTML attributes and
210
+ CSP origin must match exactly. Deployments permit a frozen set of canonical
211
+ origins rather than a boolean, so enabling one CDN cannot authorize a future
212
+ provider host. Local providers and the no-UI deployment need no permission.
213
+ Repository-wide tests verify the vendored manifests, hashes, licenses,
214
+ packaging tree and absence of UI runtime dependencies. See ADR 0040.
215
+
216
+ The design baseline is ASGI 3.0 and the HTTP/WebSocket sub-specification 2.5:
217
+
218
+ - https://asgi.readthedocs.io/en/latest/specs/main.html
219
+ - https://asgi.readthedocs.io/en/latest/specs/www.html
220
+
221
+ This is not a complete ASGI/HTTP, OpenAPI or WCAG conformance claim. The public
222
+ composition API is experimental; see RFC 0003, ADR 0018, ADR 0071 and ADR 0072.
223
+
224
+ - Import package: `agnara_http`
225
+ - Depends on: the exact synchronized `agnara` version
226
+ - Must not import: sibling adapter packages
227
+
228
+ See `ARCHITECTURE.md` sections 3 and 4 for the package boundaries and the
229
+ allowed dependency graph.
230
+
231
+ ## Discovery endpoint
232
+
233
+ The introspection snapshot is served through a surface that is authorized by
234
+ construction rather than by configuration. It takes a principal resolver — the
235
+ application's authentication boundary, since this package verifies no
236
+ credential — and answers `401` with a declared challenge to an unidentified
237
+ viewer unless anonymous discovery is opted into explicitly.
238
+
239
+ Filtering happens per request, before serialization, so a document is never
240
+ built for one viewer and reused for another. `public`, `s-maxage` and
241
+ `immutable` are refused at startup because a viewer-specific document must not
242
+ be shared-cacheable, `Vary` is always sent, and the default is
243
+ `private, no-store`. A resolver that raises produces a redacted `500` rather
244
+ than being read as anonymous.
245
+
246
+ The body is the same document `agnara inspect --json` produces. Seeing a
247
+ capability here authorizes nothing: invocation still runs the normal policy
248
+ pipeline. See ADR 0049.
249
+
250
+ ## Agnara Explorer
251
+
252
+ A read-only, server-rendered view of the same filtered snapshot the discovery
253
+ endpoint serves, with no JavaScript, no stylesheet and no external asset. That
254
+ makes read-only structural rather than configured and lets the content security
255
+ policy be `default-src 'none'` with no exceptions.
256
+
257
+ Navigation is project → application → capability. The index lists
258
+ applications, transport availability — including transports OpenAPI cannot
259
+ describe — and every visible capability. An application page carries its
260
+ provider graph; a capability page renders each published input's JSON Schema as
261
+ nested structure rather than as escaped JSON. A capability hidden from the viewer and one that does not
262
+ exist produce the same `404`, because telling them apart would publish the
263
+ existence of something withheld.
264
+
265
+ The shell has browser accessibility and navigation smoke coverage, but remains
266
+ unreachable from public composition. See ADR 0052 and `docs/MATURITY.md`.