pgc-transport 6.0.0__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.
@@ -0,0 +1,67 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ Copyright 2026 Bhash Ganti aka Bachi
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
18
+
19
+
20
+ ------------------------
21
+ FULL LICENSE TEXT BELOW
22
+ ------------------------
23
+
24
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
25
+
26
+ 1. Definitions.
27
+ "License" shall mean the terms and conditions for use, reproduction,
28
+ and distribution as defined by Sections 1 through 9 of this document.
29
+ "Licensor" shall mean the copyright owner.
30
+ "Legal Entity" shall mean the union of the acting entity and all other
31
+ entities that control, are controlled by, or are under common control.
32
+ "You" shall mean an individual or Legal Entity exercising permissions.
33
+ "Source" form shall mean the preferred form for making modifications.
34
+ "Object" form shall mean any form resulting from mechanical transformation.
35
+ "Work" shall mean the work of authorship.
36
+ "Derivative Works" shall mean any work based on the Work.
37
+ "Contribution" shall mean any work intentionally submitted.
38
+ "Contributor" shall mean Licensor and any individual submitting Contributions.
39
+
40
+ 2. Grant of Copyright License.
41
+ Each Contributor grants You a perpetual, worldwide, non-exclusive,
42
+ no-charge, royalty-free copyright license.
43
+
44
+ 3. Grant of Patent License.
45
+ Each Contributor grants a patent license to make, use, sell, etc.
46
+
47
+ 4. Redistribution.
48
+ You may reproduce and distribute copies provided that:
49
+ - You include a copy of this License
50
+ - You retain notices
51
+ - You state modifications
52
+ - You include NOTICE file if present
53
+
54
+ 5. Submission of Contributions.
55
+ Contributions are under this License unless stated otherwise.
56
+
57
+ 6. Trademarks.
58
+ This License does not grant trademark rights.
59
+
60
+ 7. Disclaimer of Warranty.
61
+ Provided "AS IS", without warranties.
62
+
63
+ 8. Limitation of Liability.
64
+ No liability for damages.
65
+
66
+ 9. Accepting Warranty or Additional Liability.
67
+ You may offer support/warranty on your own behalf only.
@@ -0,0 +1,11 @@
1
+ PGC — Protocol-Governed Computing
2
+ Copyright 2026 Bhash Ganti aka Bachi
3
+
4
+ This project introduces a protocol-first execution model in which:
5
+
6
+ - Behavior is declared in protocol artifacts
7
+ - Execution is performed by a deterministic runtime
8
+ - Capability implementations are bound at compile time
9
+ - Governance is enforced through invariants and assertions
10
+
11
+ Extensibility is achieved by declaration, not refactor.
@@ -0,0 +1,131 @@
1
+ Metadata-Version: 2.4
2
+ Name: pgc-transport
3
+ Version: 6.0.0
4
+ Summary: Protocol-Governed Computing — domain-neutral transport boundary engine (canonical contracts, generic resolver, protocol adapters)
5
+ Author-email: Bhash Ganti <bachipeachy@gmail.com>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://omnibachi.org/
8
+ Project-URL: Repository, https://github.com/protocol-governed-computing/protocol_transport
9
+ Keywords: protocol-governed-computing,pgc,transport,ingress,egress,http,gateway
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Topic :: Software Development
18
+ Classifier: Topic :: Internet :: WWW/HTTP
19
+ Requires-Python: >=3.10
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ License-File: NOTICE
23
+ Dynamic: license-file
24
+
25
+ # protocol_transport
26
+
27
+ **The PGC transport boundary engine** — a domain-neutral, protocol-neutral governed
28
+ boundary between external interaction protocols (HTTP, RPC, CLI) and a PGC execution
29
+ universe.
30
+
31
+ It knows *how* to transport. It declares nothing about *what* is transported: every route,
32
+ operation, and workflow binding arrives as configuration the engine is **pointed at**, never
33
+ hardcoded. The domain/platform declares what may be transported.
34
+
35
+ > **Semantic authority:** [`TRANSPORT_STANDARD_V0.md`](TRANSPORT_STANDARD_V0.md)
36
+ > (Phase-1 frozen). Read it first. For working in this repo, see
37
+ > [`CLAUDE.md`](CLAUDE.md).
38
+
39
+ ---
40
+
41
+ ## The invariant
42
+
43
+ > Many external protocols may bind to one **Operation Identity**; one Operation Identity
44
+ > resolves to one governed PGC invocation contract; the external protocol is replaceable,
45
+ > while the governed interaction remains stable.
46
+
47
+ ```
48
+ HTTP / RPC / CLI
49
+ │ (external protocol)
50
+ ▼
51
+ Protocol Adapter ─ translates wire ↔ Canonical Transport Request/Response (owns no domain meaning)
52
+ ▼
53
+ Operation Identity ─ stable public name (e.g. collatz.compute); NEVER a workflow id
54
+ ▼
55
+ TI → handler → TE ─ ingress contract → invocation → egress classification
56
+ ▼
57
+ Canonical Response ─ protocol-neutral { outcome, result_class, result, evidence, errors }
58
+ ▼
59
+ Response Projection ─ adapter maps Result Class → HTTP status / RPC error / CLI exit
60
+ ```
61
+
62
+ `TI`/`TE` and the workflows they bind to are **domain-governed and live outside this repo**
63
+ (in the platform/domain surface). This repo is only the engine.
64
+
65
+ ---
66
+
67
+ ## Layout
68
+
69
+ | Path | Role |
70
+ |------|------|
71
+ | `contracts/` | Canonical, protocol-neutral interop objects — `TRANSPORT_REQUEST_V0`, `TRANSPORT_RESPONSE_V0` |
72
+ | `pgc_transport/request.py` | The Canonical Transport Request every adapter builds. No protocol in it. |
73
+ | `pgc_transport/resolver/` | The generic resolution engine: `operation → TI → handler → TE → Canonical Response`. Zero domain knowledge. |
74
+ | `pgc_transport/adapters/http/` | HTTP mechanics: static mounts, the External Protocol Binding table (data), Response Projection |
75
+ | `pgc_transport/adapters/cli/` | The `pgc_transport` command: one request from the command line, and the HTTP adapter's launcher |
76
+ | `TRANSPORT_STANDARD_V0.md` | The frozen semantic model |
77
+ | `run_http.sh` | Domain-neutral launcher (env-provisioned; names no workload) |
78
+
79
+ ---
80
+
81
+ ## The acid test
82
+
83
+ The engine holds **no domain knowledge**. Grep `pgc_transport/` for any workload
84
+ or field name (`collatz`, `number`, `steps`, a route literal) and you find nothing. Everything
85
+ domain-specific is data the engine is pointed at:
86
+
87
+ - **operations** (`TI`/`TE` declarations) via `PGC_OPERATIONS_ROOTS`
88
+ - **routes → operations** via the External Protocol Binding JSON (`PGC_HTTP_BINDINGS`)
89
+ - **static content** via `PGC_STATIC_MOUNTS`
90
+
91
+ Adding an operation is additive — a new `TI`/`TE` pair plus a binding line — with **no engine
92
+ change**.
93
+
94
+ ---
95
+
96
+ ## Running
97
+
98
+ `run_http.sh` is generic and requires the environment to point it at roots. A concrete
99
+ composition (the platform reference surface) lives at
100
+ `conformance_workloads/workloads/collatz/client/serve.sh`, which supplies every `PGC_*` variable and execs this
101
+ launcher:
102
+
103
+ ```bash
104
+ ./conformance_workloads/workloads/collatz/client/serve.sh # serves the reference surface on :8000
105
+ ```
106
+
107
+ Installed as `pgc-transport`, the same boundary is the `pgc_transport` command. It is pointed at a
108
+ snapshot and an instance root, like the HTTP adapter:
109
+
110
+ ```bash
111
+ export PGC_SNAPSHOT_ROOT=snapshot PGC_DATA_ROOT=data
112
+ pgc_transport call collatz.compute '{"number": 27}' # prints the Canonical Transport Response
113
+ pgc_transport http # serves the HTTP adapter, as run_http.sh does
114
+ ```
115
+
116
+ `call` exits with the adapter's projection of the Result Class: `0` SUCCESS, `1` VIOLATION, `3`
117
+ UNAUTHORIZED, `4` NOT_FOUND, `5` OPERATION_NOT_FOUND, `6` EXECUTION_FAILURE. `2` means the
118
+ invocation itself was malformed and never reached the boundary.
119
+
120
+ See [`CLAUDE.md`](CLAUDE.md) for the full configuration surface and architectural rules.
121
+
122
+ ---
123
+
124
+ ## Layer position
125
+
126
+ ```
127
+ external world → transport (adapter → resolver) → runtime (executes) → transport (TE → adapter) → external world
128
+ ```
129
+
130
+ The engine's only cross-repo dependency is the runtime **execution interface**
131
+ (`runtime.api.invoke_workflow`) — it never imports the compiler, protocol, or any domain.
@@ -0,0 +1,107 @@
1
+ # protocol_transport
2
+
3
+ **The PGC transport boundary engine** — a domain-neutral, protocol-neutral governed
4
+ boundary between external interaction protocols (HTTP, RPC, CLI) and a PGC execution
5
+ universe.
6
+
7
+ It knows *how* to transport. It declares nothing about *what* is transported: every route,
8
+ operation, and workflow binding arrives as configuration the engine is **pointed at**, never
9
+ hardcoded. The domain/platform declares what may be transported.
10
+
11
+ > **Semantic authority:** [`TRANSPORT_STANDARD_V0.md`](TRANSPORT_STANDARD_V0.md)
12
+ > (Phase-1 frozen). Read it first. For working in this repo, see
13
+ > [`CLAUDE.md`](CLAUDE.md).
14
+
15
+ ---
16
+
17
+ ## The invariant
18
+
19
+ > Many external protocols may bind to one **Operation Identity**; one Operation Identity
20
+ > resolves to one governed PGC invocation contract; the external protocol is replaceable,
21
+ > while the governed interaction remains stable.
22
+
23
+ ```
24
+ HTTP / RPC / CLI
25
+ │ (external protocol)
26
+ ▼
27
+ Protocol Adapter ─ translates wire ↔ Canonical Transport Request/Response (owns no domain meaning)
28
+ ▼
29
+ Operation Identity ─ stable public name (e.g. collatz.compute); NEVER a workflow id
30
+ ▼
31
+ TI → handler → TE ─ ingress contract → invocation → egress classification
32
+ ▼
33
+ Canonical Response ─ protocol-neutral { outcome, result_class, result, evidence, errors }
34
+ ▼
35
+ Response Projection ─ adapter maps Result Class → HTTP status / RPC error / CLI exit
36
+ ```
37
+
38
+ `TI`/`TE` and the workflows they bind to are **domain-governed and live outside this repo**
39
+ (in the platform/domain surface). This repo is only the engine.
40
+
41
+ ---
42
+
43
+ ## Layout
44
+
45
+ | Path | Role |
46
+ |------|------|
47
+ | `contracts/` | Canonical, protocol-neutral interop objects — `TRANSPORT_REQUEST_V0`, `TRANSPORT_RESPONSE_V0` |
48
+ | `pgc_transport/request.py` | The Canonical Transport Request every adapter builds. No protocol in it. |
49
+ | `pgc_transport/resolver/` | The generic resolution engine: `operation → TI → handler → TE → Canonical Response`. Zero domain knowledge. |
50
+ | `pgc_transport/adapters/http/` | HTTP mechanics: static mounts, the External Protocol Binding table (data), Response Projection |
51
+ | `pgc_transport/adapters/cli/` | The `pgc_transport` command: one request from the command line, and the HTTP adapter's launcher |
52
+ | `TRANSPORT_STANDARD_V0.md` | The frozen semantic model |
53
+ | `run_http.sh` | Domain-neutral launcher (env-provisioned; names no workload) |
54
+
55
+ ---
56
+
57
+ ## The acid test
58
+
59
+ The engine holds **no domain knowledge**. Grep `pgc_transport/` for any workload
60
+ or field name (`collatz`, `number`, `steps`, a route literal) and you find nothing. Everything
61
+ domain-specific is data the engine is pointed at:
62
+
63
+ - **operations** (`TI`/`TE` declarations) via `PGC_OPERATIONS_ROOTS`
64
+ - **routes → operations** via the External Protocol Binding JSON (`PGC_HTTP_BINDINGS`)
65
+ - **static content** via `PGC_STATIC_MOUNTS`
66
+
67
+ Adding an operation is additive — a new `TI`/`TE` pair plus a binding line — with **no engine
68
+ change**.
69
+
70
+ ---
71
+
72
+ ## Running
73
+
74
+ `run_http.sh` is generic and requires the environment to point it at roots. A concrete
75
+ composition (the platform reference surface) lives at
76
+ `conformance_workloads/workloads/collatz/client/serve.sh`, which supplies every `PGC_*` variable and execs this
77
+ launcher:
78
+
79
+ ```bash
80
+ ./conformance_workloads/workloads/collatz/client/serve.sh # serves the reference surface on :8000
81
+ ```
82
+
83
+ Installed as `pgc-transport`, the same boundary is the `pgc_transport` command. It is pointed at a
84
+ snapshot and an instance root, like the HTTP adapter:
85
+
86
+ ```bash
87
+ export PGC_SNAPSHOT_ROOT=snapshot PGC_DATA_ROOT=data
88
+ pgc_transport call collatz.compute '{"number": 27}' # prints the Canonical Transport Response
89
+ pgc_transport http # serves the HTTP adapter, as run_http.sh does
90
+ ```
91
+
92
+ `call` exits with the adapter's projection of the Result Class: `0` SUCCESS, `1` VIOLATION, `3`
93
+ UNAUTHORIZED, `4` NOT_FOUND, `5` OPERATION_NOT_FOUND, `6` EXECUTION_FAILURE. `2` means the
94
+ invocation itself was malformed and never reached the boundary.
95
+
96
+ See [`CLAUDE.md`](CLAUDE.md) for the full configuration surface and architectural rules.
97
+
98
+ ---
99
+
100
+ ## Layer position
101
+
102
+ ```
103
+ external world → transport (adapter → resolver) → runtime (executes) → transport (TE → adapter) → external world
104
+ ```
105
+
106
+ The engine's only cross-repo dependency is the runtime **execution interface**
107
+ (`runtime.api.invoke_workflow`) — it never imports the compiler, protocol, or any domain.
@@ -0,0 +1 @@
1
+ """PGC transport boundary: the protocol-neutral resolver and its protocol adapters."""
@@ -0,0 +1 @@
1
+ """Protocol adapters (Layer 2) — thin, non-authorial boundary translators."""
@@ -0,0 +1 @@
1
+ """Command-line protocol adapter."""
@@ -0,0 +1,99 @@
1
+ """Command-line Protocol Adapter — the `pgc_transport` command.
2
+
3
+ Layer 2 (Transport Standard V0), the command-line counterpart of the HTTP adapter:
4
+
5
+ pgc_transport call <operation> '<input as JSON>'
6
+ Translate one invocation into a Canonical Transport Request, hand it to the
7
+ protocol-neutral resolver, and print the Canonical Transport Response as JSON. The
8
+ exit status is this adapter's Response Projection (§7, RESPONSE_PROJECTION_EXTERNAL):
9
+ the Result-Class → exit-status table lives HERE, never in a TE.
10
+
11
+ pgc_transport http
12
+ Serve the HTTP adapter (`pgc_transport.adapters.http.server`), configured exactly as `run_http.sh`
13
+ configures it.
14
+
15
+ Both are POINTED at everything through the environment (ADAPTER_NON_AUTHORIAL):
16
+ PGC_SNAPSHOT_ROOT the assembled snapshot the boundary is read from
17
+ PGC_DATA_ROOT the instance root execution writes traces and stored state under
18
+ plus, for `http`, the variables `run_http.sh` documents. Nothing here names a workload,
19
+ an operation or a field: the operation is whatever the caller names, and the snapshot's TI
20
+ alone decides whether to admit its input.
21
+
22
+ Stdlib only.
23
+ """
24
+ from __future__ import annotations
25
+
26
+ import argparse
27
+ import json
28
+ import os
29
+ import sys
30
+ from pathlib import Path
31
+
32
+ from pgc_transport.request import to_canonical_request
33
+ from pgc_transport.resolver.registry import load_registry
34
+ from pgc_transport.resolver.resolver import resolve
35
+
36
+ # Response Projection (adapter-owned; §7). Result Class -> exit status. Distinct statuses keep
37
+ # the classes apart for a caller that cannot read the body; the body carries the class itself.
38
+ _EXIT_STATUS = {
39
+ "SUCCESS": 0,
40
+ "VIOLATION": 1,
41
+ "UNAUTHORIZED": 3,
42
+ "NOT_FOUND": 4,
43
+ "OPERATION_NOT_FOUND": 5,
44
+ "EXECUTION_FAILURE": 6,
45
+ }
46
+ _USAGE = 2 # as argparse uses it: the invocation never reached the boundary
47
+
48
+
49
+ def _refuse(message: str) -> None:
50
+ print(f"pgc_transport: {message}", file=sys.stderr)
51
+ raise SystemExit(_USAGE)
52
+
53
+
54
+ def _required_env(name: str) -> str:
55
+ value = os.environ.get(name)
56
+ if not value:
57
+ _refuse(f"{name} is not set. The adapter reads nothing it is not pointed at.")
58
+ return value
59
+
60
+
61
+ def _call(operation: str, raw_input: str) -> int:
62
+ try:
63
+ canonical_input = json.loads(raw_input)
64
+ except json.JSONDecodeError as exc:
65
+ _refuse(f"the input is not JSON: {exc}")
66
+ if not isinstance(canonical_input, dict):
67
+ _refuse("the input must be a JSON object")
68
+
69
+ snapshot_root = _required_env("PGC_SNAPSHOT_ROOT")
70
+ data_root = _required_env("PGC_DATA_ROOT")
71
+ registry = load_registry(Path(snapshot_root))
72
+ response = resolve(to_canonical_request(operation, canonical_input), registry,
73
+ data_root=data_root, snapshot_root=snapshot_root)
74
+ json.dump(response, sys.stdout, indent=2)
75
+ sys.stdout.write("\n")
76
+ return _EXIT_STATUS.get(response["result_class"], _EXIT_STATUS["EXECUTION_FAILURE"])
77
+
78
+
79
+ def main(argv: list[str] | None = None) -> int:
80
+ parser = argparse.ArgumentParser(
81
+ prog="pgc_transport",
82
+ description="The PGC transport boundary, from the command line.")
83
+ commands = parser.add_subparsers(dest="command", required=True, metavar="<command>")
84
+ call = commands.add_parser("call", help="send one request through the boundary")
85
+ call.add_argument("operation", help="the Operation Identity, e.g. one a TI in the snapshot declares")
86
+ call.add_argument("input", nargs="?", default="{}", help="the operation's input, a JSON object")
87
+ commands.add_parser("http", help="serve the HTTP adapter")
88
+ args = parser.parse_args(argv)
89
+
90
+ if args.command == "call":
91
+ return _call(args.operation, args.input)
92
+ # Imported only when asked for: the HTTP adapter reads its configuration at import.
93
+ from pgc_transport.adapters.http import server
94
+ server.main()
95
+ return 0
96
+
97
+
98
+ if __name__ == "__main__":
99
+ raise SystemExit(main())
@@ -0,0 +1 @@
1
+ """HTTP protocol adapter (cut #1)."""
@@ -0,0 +1,81 @@
1
+ """External Protocol Binding (HTTP) — adapter-owned config, NOT a PGC artifact (Patch 2).
2
+
3
+ The binding is **data**: a table of entries loaded from a JSON file the adapter is pointed at.
4
+ It maps an HTTP protocol selector (method + route) to an Operation Identity. It is deliberately
5
+ *not* imperative routing logic, so it can later be promoted to a governed `XB_` artifact with no
6
+ change to the transport engine.
7
+
8
+ Two entry forms coexist, both governed — an operation resolves only if a matching TI/TE pair is
9
+ registered in the snapshot:
10
+
11
+ fixed operation {"method", "path", "operation"}
12
+ operation in body {"method", "path", "operation_in_body": true, "namespace": "…"}
13
+
14
+ The second form exists because a catalog of many sibling identities would otherwise need one
15
+ route each. **The namespace is an ADMISSION CONSTRAINT, not a dispatcher.** The adapter checks
16
+ that the submitted identity falls inside the declared namespace and does nothing else with it:
17
+ the value is passed through as an Operation Identity and resolved through the same governed
18
+ registry as a fixed operation, against its own TI/TE pair. Branching on operation semantics here
19
+ is forbidden (`ADAPTER_NON_AUTHORIAL`) — that would make the adapter an RPC router.
20
+
21
+ This module contains no workload knowledge — the concrete routes, operations and namespaces live
22
+ entirely in the JSON data file.
23
+ """
24
+ from __future__ import annotations
25
+
26
+ import json
27
+ from pathlib import Path
28
+ from typing import Any, NamedTuple
29
+
30
+
31
+ class Binding(NamedTuple):
32
+ """One bound route. Exactly one of `operation` / `namespace` is meaningful."""
33
+
34
+ operation: str | None # fixed form: the identity this route always carries
35
+ namespace: str | None # in-body form: the namespace an identity must fall inside
36
+
37
+
38
+ class AdmissionError(ValueError):
39
+ """The submitted identity is absent or outside the namespace this route admits."""
40
+
41
+
42
+ def load_bindings(path: Path) -> dict[tuple[str, str], Binding]:
43
+ """Load the HTTP binding table → {(METHOD, path): Binding}."""
44
+ data = json.loads(path.read_text(encoding="utf-8"))
45
+ table: dict[tuple[str, str], Binding] = {}
46
+ for entry in data.get("bindings", []):
47
+ key = (entry["method"].upper(), entry["path"])
48
+ if key in table:
49
+ raise ValueError(f"duplicate HTTP binding for {key}")
50
+ if entry.get("operation_in_body"):
51
+ namespace = entry.get("namespace")
52
+ if not namespace:
53
+ raise ValueError(f"binding {key} declares operation_in_body without a namespace")
54
+ table[key] = Binding(operation=None, namespace=namespace)
55
+ else:
56
+ table[key] = Binding(operation=entry["operation"], namespace=None)
57
+ return table
58
+
59
+
60
+ def select_operation(binding: Binding, body: dict[str, Any]) -> tuple[str, dict[str, Any]]:
61
+ """Determine (Operation Identity, canonical input) for a bound route.
62
+
63
+ Fixed form: the route carries the identity and the whole body IS the input.
64
+ In-body form: the body carries `operation` plus its `params`, and the identity is admitted
65
+ iff it falls inside the route's declared namespace. The check is textual containment — the
66
+ adapter neither knows nor asks what the identity means.
67
+ """
68
+ if binding.operation is not None:
69
+ return binding.operation, body
70
+
71
+ operation = body.get("operation")
72
+ if not isinstance(operation, str) or not operation:
73
+ raise AdmissionError("request body declares no operation identity")
74
+ if not operation.startswith(binding.namespace or ""):
75
+ raise AdmissionError(
76
+ f"operation '{operation}' is outside the namespace "
77
+ f"'{binding.namespace}' admitted by this route"
78
+ )
79
+ params = body.get("params")
80
+ return operation, params if isinstance(params, dict) else {}
81
+