trustplane-auth-sdk 0.1.0rc1__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 (28) hide show
  1. trustplane_auth_sdk-0.1.0rc1/.github/workflows/pr.yml +43 -0
  2. trustplane_auth_sdk-0.1.0rc1/.github/workflows/release-pypi.yml +226 -0
  3. trustplane_auth_sdk-0.1.0rc1/.gitignore +9 -0
  4. trustplane_auth_sdk-0.1.0rc1/AGENTS.md +8 -0
  5. trustplane_auth_sdk-0.1.0rc1/LICENSE +51 -0
  6. trustplane_auth_sdk-0.1.0rc1/PKG-INFO +137 -0
  7. trustplane_auth_sdk-0.1.0rc1/README.md +110 -0
  8. trustplane_auth_sdk-0.1.0rc1/SECURITY.md +7 -0
  9. trustplane_auth_sdk-0.1.0rc1/docs/release-pypi.md +105 -0
  10. trustplane_auth_sdk-0.1.0rc1/pyproject.toml +61 -0
  11. trustplane_auth_sdk-0.1.0rc1/scripts/scan-leaks.sh +7 -0
  12. trustplane_auth_sdk-0.1.0rc1/scripts/scan-wording.sh +7 -0
  13. trustplane_auth_sdk-0.1.0rc1/scripts/smoke-wheel.sh +26 -0
  14. trustplane_auth_sdk-0.1.0rc1/src/trustplane_auth/__init__.py +49 -0
  15. trustplane_auth_sdk-0.1.0rc1/src/trustplane_auth/py.typed +1 -0
  16. trustplane_auth_sdk-0.1.0rc1/src/trustplane_auth/signing.py +274 -0
  17. trustplane_auth_sdk-0.1.0rc1/src/trustplane_auth/transcript.py +369 -0
  18. trustplane_auth_sdk-0.1.0rc1/testdata/conformance/v1/README.md +11 -0
  19. trustplane_auth_sdk-0.1.0rc1/testdata/conformance/v1/body-sha256-v1.json +18 -0
  20. trustplane_auth_sdk-0.1.0rc1/testdata/conformance/v1/broker-ipc-v1.denied.json +90 -0
  21. trustplane_auth_sdk-0.1.0rc1/testdata/conformance/v1/broker-ipc-v1.success.json +94 -0
  22. trustplane_auth_sdk-0.1.0rc1/testdata/conformance/v1/bundle-v1.source-rules.json +97 -0
  23. trustplane_auth_sdk-0.1.0rc1/testdata/conformance/v1/manifest.json +68 -0
  24. trustplane_auth_sdk-0.1.0rc1/testdata/conformance/v1/passport-v1.minimal-valid-claims.json +35 -0
  25. trustplane_auth_sdk-0.1.0rc1/testdata/conformance/v1/signer-taxonomy-v1.json +31 -0
  26. trustplane_auth_sdk-0.1.0rc1/testdata/conformance/v1/transcript-v1.ambiguous-query-headers.json +109 -0
  27. trustplane_auth_sdk-0.1.0rc1/testdata/conformance/v1/transcript-v1.json +109 -0
  28. trustplane_auth_sdk-0.1.0rc1/tests/test_sdk.py +503 -0
@@ -0,0 +1,43 @@
1
+ name: pr
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches:
7
+ - main
8
+ - "feature/**"
9
+
10
+ jobs:
11
+ checks:
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ python-version: ["3.10", "3.11", "3.12"]
17
+
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ - uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+ cache: pip
24
+ - name: Upgrade pip
25
+ run: python -m pip install -U pip
26
+ - name: Install package and dev dependencies
27
+ run: python -m pip install -e ".[dev]"
28
+ - name: Format check
29
+ run: ruff format --check .
30
+ - name: Lint
31
+ run: ruff check .
32
+ - name: Type check
33
+ run: mypy
34
+ - name: Tests
35
+ run: pytest
36
+ - name: Build sdist and wheel
37
+ run: python -m build
38
+ - name: Clean wheel install/import smoke
39
+ run: scripts/smoke-wheel.sh
40
+ - name: Leak scan
41
+ run: scripts/scan-leaks.sh
42
+ - name: Wording scan
43
+ run: scripts/scan-wording.sh
@@ -0,0 +1,226 @@
1
+ name: Manual PyPI Release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ version:
7
+ description: TrustPlane release tag to prepare, for example v0.1.0-rc.1
8
+ required: true
9
+ type: string
10
+ publish_package:
11
+ description: Publish to PyPI, create the release commit, and push the annotated tag
12
+ required: true
13
+ default: false
14
+ type: boolean
15
+
16
+ permissions:
17
+ contents: write
18
+ id-token: write
19
+
20
+ env:
21
+ PACKAGE_NAME: trustplane-auth-sdk
22
+
23
+ jobs:
24
+ release:
25
+ runs-on: ubuntu-latest
26
+ steps:
27
+ - name: Require main
28
+ run: |
29
+ if [ "${GITHUB_REF_NAME}" != "main" ]; then
30
+ echo "release workflow must run from main"
31
+ exit 1
32
+ fi
33
+ - uses: actions/checkout@v4
34
+ with:
35
+ fetch-depth: 0
36
+ - uses: actions/setup-python@v5
37
+ with:
38
+ python-version: "3.12"
39
+ cache: pip
40
+ - name: Upgrade pip
41
+ run: python -m pip install -U pip
42
+ - name: Install package and dev dependencies
43
+ run: python -m pip install -e ".[dev]"
44
+ - name: Configure release git identity
45
+ run: |
46
+ git config user.name "Medh Mesh"
47
+ git config user.email "maintainer@trustplane.dev"
48
+ - name: Validate version input
49
+ run: |
50
+ if ! printf '%s\n' "${{ inputs.version }}" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?$'; then
51
+ echo "version must look like v0.1.0 or v0.1.0-rc.1"
52
+ exit 1
53
+ fi
54
+ python - <<'PY'
55
+ import os
56
+ import re
57
+
58
+ version = os.environ["INPUT_VERSION"]
59
+ match = re.fullmatch(r"v([0-9]+)\.([0-9]+)\.([0-9]+)(?:-rc\.([0-9]+))?", version)
60
+ if match is None:
61
+ raise SystemExit("invalid version")
62
+ major, minor, patch, rc = match.groups()
63
+ package_version = f"{major}.{minor}.{patch}"
64
+ if rc is not None:
65
+ package_version = f"{package_version}rc{rc}"
66
+ with open(os.environ["GITHUB_ENV"], "a", encoding="utf-8") as env:
67
+ env.write(f"PYTHON_PACKAGE_VERSION={package_version}\n")
68
+ print(f"TrustPlane version: {version}")
69
+ print(f"Python package version: {package_version}")
70
+ PY
71
+ env:
72
+ INPUT_VERSION: ${{ inputs.version }}
73
+ - name: Refuse existing git tag
74
+ run: |
75
+ if git rev-parse -q --verify "refs/tags/${{ inputs.version }}" >/dev/null; then
76
+ echo "local tag already exists: ${{ inputs.version }}"
77
+ exit 1
78
+ fi
79
+ if git ls-remote --exit-code --tags origin "refs/tags/${{ inputs.version }}" >/dev/null 2>&1; then
80
+ echo "remote tag already exists: ${{ inputs.version }}"
81
+ exit 1
82
+ fi
83
+ - name: Refuse existing PyPI version
84
+ run: |
85
+ python - <<'PY'
86
+ import json
87
+ import os
88
+ import sys
89
+ import urllib.error
90
+ import urllib.request
91
+
92
+ package_name = os.environ["PACKAGE_NAME"]
93
+ package_version = os.environ["PYTHON_PACKAGE_VERSION"]
94
+ url = f"https://pypi.org/pypi/{package_name}/json"
95
+ try:
96
+ with urllib.request.urlopen(url, timeout=20) as response:
97
+ data = json.load(response)
98
+ except urllib.error.HTTPError as exc:
99
+ if exc.code == 404:
100
+ print(f"PyPI project does not exist yet: {package_name}")
101
+ sys.exit(0)
102
+ raise
103
+
104
+ if package_version in data.get("releases", {}):
105
+ raise SystemExit(f"PyPI version already exists: {package_name}=={package_version}")
106
+ print(f"PyPI version is unused: {package_name}=={package_version}")
107
+ PY
108
+ - name: Format check
109
+ run: ruff format --check .
110
+ - name: Lint
111
+ run: ruff check .
112
+ - name: Type check
113
+ run: mypy
114
+ - name: Tests
115
+ run: pytest
116
+ - name: Leak scan
117
+ run: scripts/scan-leaks.sh
118
+ - name: Wording scan
119
+ run: scripts/scan-wording.sh
120
+ - name: Verify clean worktree before version update
121
+ run: |
122
+ status="$(git status --short)"
123
+ printf '%s\n' "$status"
124
+ if [ -n "$status" ]; then
125
+ echo "worktree must be clean before version update"
126
+ exit 1
127
+ fi
128
+ - name: Set package version and README install command
129
+ run: |
130
+ python - <<'PY'
131
+ import os
132
+ from pathlib import Path
133
+
134
+ package_version = os.environ["PYTHON_PACKAGE_VERSION"]
135
+ path = Path("pyproject.toml")
136
+ lines = path.read_text(encoding="utf-8").splitlines()
137
+ changed = False
138
+ for index, line in enumerate(lines):
139
+ if line.startswith("version = "):
140
+ lines[index] = f'version = "{package_version}"'
141
+ changed = True
142
+ break
143
+ if not changed:
144
+ raise SystemExit("pyproject.toml project version not found")
145
+ path.write_text("\n".join(lines) + "\n", encoding="utf-8")
146
+
147
+ readme = Path("README.md")
148
+ text = readme.read_text(encoding="utf-8")
149
+ prefix, install_marker, rest = text.partition("## Install\n")
150
+ old_install, package_marker, suffix = rest.partition("## Package Name\n")
151
+ if install_marker == "" or package_marker == "":
152
+ raise SystemExit("README package section marker not found")
153
+ install_section = (
154
+ "## Install\n\n"
155
+ "Install released preview versions from PyPI with an exact version pin:\n\n"
156
+ "```sh\n"
157
+ f"python -m pip install trustplane-auth-sdk=={package_version}\n"
158
+ "```\n\n"
159
+ "For unreleased local changes, test this repository through a local checkout or built wheel.\n\n"
160
+ )
161
+ readme.write_text(prefix + install_section + package_marker + suffix, encoding="utf-8")
162
+ PY
163
+ - name: Verify only release metadata is dirty
164
+ run: |
165
+ status="$(git status --short)"
166
+ printf '%s\n' "$status"
167
+ python - <<'PY'
168
+ import subprocess
169
+
170
+ dirty = subprocess.check_output(
171
+ ["git", "diff", "--name-only"],
172
+ text=True,
173
+ ).splitlines()
174
+ if dirty != ["README.md", "pyproject.toml"]:
175
+ raise SystemExit(f"unexpected worktree state after version update: {dirty}")
176
+ PY
177
+ - name: Build sdist and wheel
178
+ run: python -m build
179
+ - name: Clean wheel install/import smoke
180
+ run: scripts/smoke-wheel.sh
181
+ - name: Dry-run summary
182
+ if: ${{ inputs.publish_package == false }}
183
+ run: |
184
+ {
185
+ echo "### PyPI release readiness complete"
186
+ echo
187
+ echo "Prepared and smoke-tested $PACKAGE_NAME==$PYTHON_PACKAGE_VERSION."
188
+ echo
189
+ echo "TrustPlane release version: ${{ inputs.version }}"
190
+ echo
191
+ echo "publish_package=false, so this run did not create a commit, tag, PyPI publish, or GitHub Release."
192
+ echo
193
+ echo "To publish later, rerun from main with publish_package=true after PyPI Trusted Publishing is configured."
194
+ } >> "$GITHUB_STEP_SUMMARY"
195
+ - name: Require PyPI Trusted Publishing
196
+ if: ${{ inputs.publish_package }}
197
+ run: |
198
+ if [ -z "${ACTIONS_ID_TOKEN_REQUEST_TOKEN:-}" ] || [ -z "${ACTIONS_ID_TOKEN_REQUEST_URL:-}" ]; then
199
+ echo "PyPI Trusted Publishing/OIDC is not available to this job."
200
+ echo "Configure PyPI Trusted Publishing for $PACKAGE_NAME and keep id-token: write enabled."
201
+ exit 1
202
+ fi
203
+ - name: Publish package with PyPI Trusted Publishing
204
+ if: ${{ inputs.publish_package }}
205
+ uses: pypa/gh-action-pypi-publish@release/v1
206
+ - name: Commit version, tag, and push
207
+ if: ${{ inputs.publish_package }}
208
+ run: |
209
+ git add README.md pyproject.toml
210
+ git commit -m "Release Python SDK ${{ inputs.version }}"
211
+ git tag -a "${{ inputs.version }}" -m "TrustPlane Auth Python SDK ${{ inputs.version }}"
212
+ git push origin "HEAD:main" "refs/tags/${{ inputs.version }}"
213
+ - name: Publish summary
214
+ if: ${{ inputs.publish_package }}
215
+ run: |
216
+ {
217
+ echo "### PyPI release published"
218
+ echo
219
+ echo "Published $PACKAGE_NAME==$PYTHON_PACKAGE_VERSION with PyPI Trusted Publishing."
220
+ echo
221
+ echo "TrustPlane release version: ${{ inputs.version }}"
222
+ echo
223
+ echo "No GitHub Release was created by this workflow. Track any GitHub Release as a follow-up release task if repo policy later requires one."
224
+ echo
225
+ echo "Recovery note: PyPI publish runs before git commit/tag push. If PyPI publish fails, there is no release commit or tag to repair. If git push fails after PyPI publish, do not republish or overwrite PyPI; repair the git commit/tag publication from the same workflow commit and package version."
226
+ } >> "$GITHUB_STEP_SUMMARY"
@@ -0,0 +1,9 @@
1
+ /.mypy_cache/
2
+ /.pytest_cache/
3
+ /.ruff_cache/
4
+ /build/
5
+ /dist/
6
+ /*.egg-info/
7
+ /.venv/
8
+ __pycache__/
9
+ *.py[cod]
@@ -0,0 +1,8 @@
1
+ # Agent Instructions
2
+
3
+ - Do not commit directly to `main`.
4
+ - Use branch `feature/python-sdk-foundation` for TP-054A work.
5
+ - Do not use a `[codex]` prefix in branch names, commits, PR titles, or PR bodies.
6
+ - Every commit must be authored and committed as `Medh Mesh <maintainer@trustplane.dev>`.
7
+ - Do not publish to PyPI, create tags, or create GitHub Releases from this repository.
8
+ - Keep public docs free of private paths, personal names, token values, internal process chatter, and premature package availability claims.
@@ -0,0 +1,51 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
10
+
11
+ "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
12
+
13
+ "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
14
+
15
+ "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
16
+
17
+ "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
18
+
19
+ "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
20
+
21
+ "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work.
22
+
23
+ "Derivative Works" shall mean any work, whether in Source or Object form, that is based on or derived from the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link or bind by name to the interfaces of the Work.
24
+
25
+ "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner.
26
+
27
+ "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
28
+
29
+ 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
30
+
31
+ 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work.
32
+
33
+ 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, provided that You meet the following conditions:
34
+
35
+ (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
36
+
37
+ (b) You must cause any modified files to carry prominent notices stating that You changed the files; and
38
+
39
+ (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
40
+
41
+ (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file.
42
+
43
+ 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License.
44
+
45
+ 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work.
46
+
47
+ 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
48
+
49
+ 8. Limitation of Liability. In no event and under no legal theory shall any Contributor be liable to You for damages arising from use of the Work.
50
+
51
+ 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer support, warranty, indemnity, or other liability obligations. In accepting such obligations, You may act only on Your own behalf and on Your sole responsibility.
@@ -0,0 +1,137 @@
1
+ Metadata-Version: 2.4
2
+ Name: trustplane-auth-sdk
3
+ Version: 0.1.0rc1
4
+ Summary: Python SDK foundation for TrustPlane Auth request material and local Ed25519 signing
5
+ Project-URL: Homepage, https://github.com/trustplane-dev/trustplane-auth-sdk-python
6
+ Project-URL: Repository, https://github.com/trustplane-dev/trustplane-auth-sdk-python
7
+ Author: TrustPlane
8
+ License-Expression: 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: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Security :: Cryptography
19
+ Requires-Python: >=3.10
20
+ Requires-Dist: cryptography>=42
21
+ Provides-Extra: dev
22
+ Requires-Dist: build>=1.2; extra == 'dev'
23
+ Requires-Dist: mypy>=1.10; extra == 'dev'
24
+ Requires-Dist: pytest>=8; extra == 'dev'
25
+ Requires-Dist: ruff>=0.5; extra == 'dev'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # TrustPlane Auth SDK for Python
29
+
30
+ Preview Python caller SDK for TrustPlane Auth request signing.
31
+
32
+ This package provides caller-side helpers for:
33
+
34
+ - building `transcript-v1` request material
35
+ - computing body SHA-256 values
36
+ - parsing passport claims needed by signing
37
+ - raw local Ed25519 software signing
38
+ - returning adapter-ready TrustPlane Auth headers
39
+
40
+ It does not include a verifier, broker, adapter, policy engine, SPIFFE issuer, deployment code, enrollment flow, bundle mutation, or TrustPlane Control API.
41
+
42
+ ## Install
43
+
44
+ Install released preview versions from PyPI with an exact version pin:
45
+
46
+ ```sh
47
+ python -m pip install trustplane-auth-sdk==0.1.0rc1
48
+ ```
49
+
50
+ For unreleased local changes, test this repository through a local checkout or built wheel.
51
+
52
+ ## Package Name
53
+
54
+ The Python distribution name candidate is `trustplane-auth-sdk`.
55
+
56
+ The import module is `trustplane_auth`:
57
+
58
+ ```python
59
+ from trustplane_auth import body_sha256, build_request, sign_request
60
+ ```
61
+
62
+ The package version remains `0.0.0` until the release workflow is introduced.
63
+
64
+ PyPI trusted publishing is planned in a follow-up.
65
+
66
+ ## Build Request Example
67
+
68
+ ```python
69
+ from trustplane_auth import SOFTWARE_KEY_BINDING, Header, build_request
70
+
71
+ material = build_request(
72
+ method="POST",
73
+ scheme="https",
74
+ authority="orders.example",
75
+ path="/v1/orders",
76
+ audience="orders-api",
77
+ route_id="orders.create",
78
+ content_encoding="identity",
79
+ body=b'{"order_id":"ord_123","amount":"42.00"}',
80
+ headers=[
81
+ Header(name="Content-Type", value="application/json"),
82
+ Header(name="X-TrustPlane-Nonce", value="nonce-v1-001"),
83
+ ],
84
+ header_allow_list=["content-type", "x-trustplane-nonce"],
85
+ passport_jti="passport-v1-minimal-001",
86
+ nonce="nonce-v1-001",
87
+ issued_at_unix=1740000000,
88
+ key_binding=SOFTWARE_KEY_BINDING,
89
+ )
90
+
91
+ print(material.transcript_sha256)
92
+ ```
93
+
94
+ ## Signing Example
95
+
96
+ Signing requires a real passport whose `cnf.key_binding` is `software` and whose `cnf.public_key_b64url` matches the Ed25519 private key.
97
+
98
+ ```python
99
+ from pathlib import Path
100
+
101
+ from trustplane_auth import Header, sign_request
102
+
103
+ passport = "header.payload.signature"
104
+ private_key_pem = Path("ed25519-private-key.pem").read_bytes()
105
+
106
+ signed = sign_request(
107
+ request={
108
+ "method": "POST",
109
+ "scheme": "https",
110
+ "authority": "orders.example",
111
+ "path": "/v1/orders",
112
+ "route_id": "orders.create",
113
+ "content_encoding": "identity",
114
+ "body": b'{"order_id":"ord_123","amount":"42.00"}',
115
+ "headers": [
116
+ Header(name="Content-Type", value="application/json"),
117
+ Header(name="X-TrustPlane-Nonce", value="nonce-v1-001"),
118
+ ],
119
+ "header_allow_list": ["content-type", "x-trustplane-nonce"],
120
+ "nonce": "nonce-v1-001",
121
+ },
122
+ passport_token=passport,
123
+ private_key=private_key_pem,
124
+ )
125
+
126
+ headers = signed.headers
127
+ ```
128
+
129
+ `sign_request` reads passport-bound fields from the passport and fails if caller-supplied consistency checks conflict. It does not infer or repair `aud`, `jti`, `iat`, `cnf.kid`, `cnf.key_binding`, or `cnf.public_key_b64url` from caller request inputs.
130
+
131
+ ## Conformance Posture
132
+
133
+ `testdata/conformance/v1` contains public-safe contract vectors copied from the TrustPlane Auth reference. Tests assert exact canonical transcript lines, transcript SHA-256 values, and body SHA-256 values.
134
+
135
+ ## Security Rule
136
+
137
+ This SDK signs only the verifier-rebuilt request transcript. Raw local signing is software-only and requires an Ed25519 private key whose public key exactly matches the passport `cnf.public_key_b64url` claim.
@@ -0,0 +1,110 @@
1
+ # TrustPlane Auth SDK for Python
2
+
3
+ Preview Python caller SDK for TrustPlane Auth request signing.
4
+
5
+ This package provides caller-side helpers for:
6
+
7
+ - building `transcript-v1` request material
8
+ - computing body SHA-256 values
9
+ - parsing passport claims needed by signing
10
+ - raw local Ed25519 software signing
11
+ - returning adapter-ready TrustPlane Auth headers
12
+
13
+ It does not include a verifier, broker, adapter, policy engine, SPIFFE issuer, deployment code, enrollment flow, bundle mutation, or TrustPlane Control API.
14
+
15
+ ## Install
16
+
17
+ Install released preview versions from PyPI with an exact version pin:
18
+
19
+ ```sh
20
+ python -m pip install trustplane-auth-sdk==0.1.0rc1
21
+ ```
22
+
23
+ For unreleased local changes, test this repository through a local checkout or built wheel.
24
+
25
+ ## Package Name
26
+
27
+ The Python distribution name candidate is `trustplane-auth-sdk`.
28
+
29
+ The import module is `trustplane_auth`:
30
+
31
+ ```python
32
+ from trustplane_auth import body_sha256, build_request, sign_request
33
+ ```
34
+
35
+ The package version remains `0.0.0` until the release workflow is introduced.
36
+
37
+ PyPI trusted publishing is planned in a follow-up.
38
+
39
+ ## Build Request Example
40
+
41
+ ```python
42
+ from trustplane_auth import SOFTWARE_KEY_BINDING, Header, build_request
43
+
44
+ material = build_request(
45
+ method="POST",
46
+ scheme="https",
47
+ authority="orders.example",
48
+ path="/v1/orders",
49
+ audience="orders-api",
50
+ route_id="orders.create",
51
+ content_encoding="identity",
52
+ body=b'{"order_id":"ord_123","amount":"42.00"}',
53
+ headers=[
54
+ Header(name="Content-Type", value="application/json"),
55
+ Header(name="X-TrustPlane-Nonce", value="nonce-v1-001"),
56
+ ],
57
+ header_allow_list=["content-type", "x-trustplane-nonce"],
58
+ passport_jti="passport-v1-minimal-001",
59
+ nonce="nonce-v1-001",
60
+ issued_at_unix=1740000000,
61
+ key_binding=SOFTWARE_KEY_BINDING,
62
+ )
63
+
64
+ print(material.transcript_sha256)
65
+ ```
66
+
67
+ ## Signing Example
68
+
69
+ Signing requires a real passport whose `cnf.key_binding` is `software` and whose `cnf.public_key_b64url` matches the Ed25519 private key.
70
+
71
+ ```python
72
+ from pathlib import Path
73
+
74
+ from trustplane_auth import Header, sign_request
75
+
76
+ passport = "header.payload.signature"
77
+ private_key_pem = Path("ed25519-private-key.pem").read_bytes()
78
+
79
+ signed = sign_request(
80
+ request={
81
+ "method": "POST",
82
+ "scheme": "https",
83
+ "authority": "orders.example",
84
+ "path": "/v1/orders",
85
+ "route_id": "orders.create",
86
+ "content_encoding": "identity",
87
+ "body": b'{"order_id":"ord_123","amount":"42.00"}',
88
+ "headers": [
89
+ Header(name="Content-Type", value="application/json"),
90
+ Header(name="X-TrustPlane-Nonce", value="nonce-v1-001"),
91
+ ],
92
+ "header_allow_list": ["content-type", "x-trustplane-nonce"],
93
+ "nonce": "nonce-v1-001",
94
+ },
95
+ passport_token=passport,
96
+ private_key=private_key_pem,
97
+ )
98
+
99
+ headers = signed.headers
100
+ ```
101
+
102
+ `sign_request` reads passport-bound fields from the passport and fails if caller-supplied consistency checks conflict. It does not infer or repair `aud`, `jti`, `iat`, `cnf.kid`, `cnf.key_binding`, or `cnf.public_key_b64url` from caller request inputs.
103
+
104
+ ## Conformance Posture
105
+
106
+ `testdata/conformance/v1` contains public-safe contract vectors copied from the TrustPlane Auth reference. Tests assert exact canonical transcript lines, transcript SHA-256 values, and body SHA-256 values.
107
+
108
+ ## Security Rule
109
+
110
+ This SDK signs only the verifier-rebuilt request transcript. Raw local signing is software-only and requires an Ed25519 private key whose public key exactly matches the passport `cnf.public_key_b64url` claim.
@@ -0,0 +1,7 @@
1
+ # Security
2
+
3
+ Please report suspected vulnerabilities through the repository security advisory flow or the TrustPlane security contact used for project disclosures.
4
+
5
+ Do not include private keys, bearer tokens, passports, or production request transcripts in public issues.
6
+
7
+ This package currently supports only raw local Ed25519 software signing. It rejects non-software key bindings for local signing.