@passioncode-ai/fabric-agent-adapter 0.6.0 → 0.6.1

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.1 - 2026-10-03
4
+
5
+ ### Fixed
6
+
7
+ - **`check_service.py` no longer fails every online service hosted behind a platform router.**
8
+ `network.host-check` required the service's own `403`, but Heroku, Fly, Render and CDNs route
9
+ by `Host`: a foreign one is answered by the router (`404`/`421`) and never reaches the process.
10
+ For a remote placement that answer now passes, unless its body is the well-known document. The
11
+ Origin and cross-site checks are unchanged — they reach the service and need its `403`. Found on
12
+ the first real online service: 26 PASS and 1 false FAIL before, 0 FAIL after (`guard_verdict`,
13
+ `test/test_fabric_service_remote.py::GuardVerdict`, 4 mutations killed).
14
+
3
15
  ## 0.6.0 - 2026-10-02
4
16
 
5
17
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@passioncode-ai/fabric-agent-adapter",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Create Fabric-compatible agents, run them as always-alive local services with dashboards (fabric-service/0.1), and adapt existing projects to the Fabric Agent Contract. This package is the installer CLI.",
5
5
  "scripts": {
6
6
  "test": "python3 test/validate.py && python3 -m unittest discover -s test -q && node --test 'test/*.test.mjs'"
@@ -3,7 +3,7 @@
3
3
  "name": "fabric-agent-adapter",
4
4
  "displayName": "Fabric Agent Adapter",
5
5
  "description": "Create new Fabric-compatible agents, build them as always-alive local services with dashboards (fabric-service/0.1), and adapt existing projects to the Fabric Agent Contract through evidence-backed MCP, A2A, or local-runner provider bundles.",
6
- "version": "0.6.0",
6
+ "version": "0.6.1",
7
7
  "author": {
8
8
  "name": "PassionCode.ai",
9
9
  "url": "https://passioncode.ai/"
@@ -5,7 +5,7 @@ license: AGPL-3.0-only OR LicenseRef-PassionCode-Commercial
5
5
  compatibility: Requires filesystem access and Python 3.9+. Exact schema checks additionally need git, Node.js, pnpm, and the pinned fabric-agent-contract checkout (a public repository). Works without those tools in an explicitly degraded structural-check mode.
6
6
  metadata:
7
7
  author: PassionCode.ai
8
- version: "0.6.0"
8
+ version: "0.6.1"
9
9
  contract-version: "0.1.0"
10
10
  contract-commit: "2ce392291c6668598d12cd38327e24696b5ca15c"
11
11
  ---
@@ -15,7 +15,7 @@ license: AGPL-3.0-only OR LicenseRef-PassionCode-Commercial
15
15
  compatibility: Python 3.9+ or Node.js 20+ for the kits; the probe needs Python 3.9+. launchd steps are macOS-only (Linux services use lifecycle manager none until a systemd adapter exists). Dashboard handoff optionally uses Fabric Dashboards MCP link/host_status/open; without it, report unresolved host capability. The contract checkout is optional.
16
16
  metadata:
17
17
  author: PassionCode.ai
18
- version: "0.6.0"
18
+ version: "0.6.1"
19
19
  contract-version: "0.1.0"
20
20
  extension: "fabric-service/0.1"
21
21
  extension-commit: "2ce392291c6668598d12cd38327e24696b5ca15c"
@@ -44,6 +44,14 @@ same objects — well-known document, events feed, login code — at the same pa
44
44
  - **Behind a router**: check the platform-set forwarded scheme; `Host` is preserved by common
45
45
  platforms and must equal the origin's host.
46
46
 
47
+ ## Checking it
48
+
49
+ `check_service.py <id>` probes a remote placement over verified TLS. Behind a platform router
50
+ (Heroku, Fly, Render, a CDN) a request that names a foreign `Host` never reaches the process: the
51
+ router answers it, usually `404` or `421`. The probe counts that as the refusal it is, as long as
52
+ the body is not the well-known document. The Origin and cross-site checks name the right `Host`,
53
+ reach the service, and still need its own `403`.
54
+
47
55
  ## What a host does and will not do
48
56
 
49
57
  | Situation | Host |
@@ -79,6 +79,23 @@ def priority_problems(plist: dict) -> list:
79
79
  return out
80
80
 
81
81
 
82
+ # A platform router (Heroku, Fly, Render, a CDN) routes by Host, so a request naming a foreign
83
+ # Host never reaches an online service: the router itself answers, typically 404 or 421. That is
84
+ # still a refusal — the service disclosed nothing — so for a remote placement the host check
85
+ # accepts it, as long as the body is not the well-known document. Every other guard request names
86
+ # the right Host, reaches the service, and must be the service's own 403.
87
+ PLATFORM_HOST_REFUSALS = (400, 404, 421)
88
+
89
+
90
+ def guard_verdict(rule: str, remote: bool, status: int, body: bytes) -> Tuple[str, str]:
91
+ if status == 403:
92
+ return "PASS", "HTTP 403"
93
+ disclosed = b"fabric-service/" in (body or b"")
94
+ if rule == "network.host-check" and remote and status in PLATFORM_HOST_REFUSALS and not disclosed:
95
+ return "PASS", "HTTP %d from the platform router: a foreign Host never reaches the service" % status
96
+ return "FAIL", "HTTP %d%s" % (status, ", and the body is the well-known document" if disclosed else "")
97
+
98
+
82
99
  class _PinnedHTTPS(http.client.HTTPSConnection):
83
100
  """HTTPS to the origin's name (SNI, certificate, Host) over a socket dialled elsewhere."""
84
101
 
@@ -243,8 +260,9 @@ class Probe:
243
260
  sent.update(self.auth_headers())
244
261
  if "Origin" in sent:
245
262
  sent["Origin"] = "https://evil.example"
246
- status, _, _ = self.request("GET", "/.well-known/fabric-service", sent)
247
- self.add(rule, "PASS" if status == 403 else "FAIL", "HTTP %d" % status)
263
+ status, _, body = self.request("GET", "/.well-known/fabric-service", sent)
264
+ verdict, evidence = guard_verdict(rule, self.remote, status, body)
265
+ self.add(rule, verdict, evidence)
248
266
  except (OSError, ssl.SSLError) as exc:
249
267
  self.add(rule, "NOT_RUN", str(exc))
250
268
  if self.remote:
@@ -5,7 +5,7 @@ license: AGPL-3.0-only OR LicenseRef-PassionCode-Commercial
5
5
  compatibility: Requires filesystem access and Python 3.9+. Exact schema checks additionally need git, Node.js, pnpm, and the pinned fabric-agent-contract checkout (a public repository). Works without those tools in an explicitly degraded structural-check mode. Ships in one plugin with adapting-projects-to-fabric, whose scripts it reuses.
6
6
  metadata:
7
7
  author: PassionCode.ai
8
- version: "0.6.0"
8
+ version: "0.6.1"
9
9
  contract-version: "0.1.0"
10
10
  contract-commit: "2ce392291c6668598d12cd38327e24696b5ca15c"
11
11
  ---