jring-client 0.5.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.
Files changed (208) hide show
  1. jring_client-0.5.0/.github/workflows/distro-smoke.yml +98 -0
  2. jring_client-0.5.0/.github/workflows/publish-pypi.yml +120 -0
  3. jring_client-0.5.0/.github/workflows/release-artifacts.yml +104 -0
  4. jring_client-0.5.0/.github/workflows/test.yml +24 -0
  5. jring_client-0.5.0/.github/workflows/uv-tool-smoke.yml +42 -0
  6. jring_client-0.5.0/CONTRIBUTING.md +55 -0
  7. jring_client-0.5.0/LICENSE +21 -0
  8. jring_client-0.5.0/MANIFEST.in +10 -0
  9. jring_client-0.5.0/PKG-INFO +670 -0
  10. jring_client-0.5.0/README.md +640 -0
  11. jring_client-0.5.0/SECURITY.md +17 -0
  12. jring_client-0.5.0/docs/ADVERSARIAL_UX_REVIEW.md +209 -0
  13. jring_client-0.5.0/docs/APK_BINDER_SPEC.md +255 -0
  14. jring_client-0.5.0/docs/APK_CALLBACK_SPEC.md +274 -0
  15. jring_client-0.5.0/docs/APK_DATA_PLATFORM_SPEC.md +220 -0
  16. jring_client-0.5.0/docs/APK_FUNCTIONAL_SPEC.md +527 -0
  17. jring_client-0.5.0/docs/APK_OTA_SPEC.md +177 -0
  18. jring_client-0.5.0/docs/APK_PLATFORM_SPEC.md +153 -0
  19. jring_client-0.5.0/docs/APK_REQUEST_SPEC.md +203 -0
  20. jring_client-0.5.0/docs/APK_SESSION_SPEC.md +105 -0
  21. jring_client-0.5.0/docs/APK_TRANSPORT_SPEC.md +241 -0
  22. jring_client-0.5.0/docs/APK_UI_SPEC.md +344 -0
  23. jring_client-0.5.0/docs/COMPATIBILITY.md +97 -0
  24. jring_client-0.5.0/docs/DESIGN.md +702 -0
  25. jring_client-0.5.0/docs/EVENT_CONTRACTS.md +119 -0
  26. jring_client-0.5.0/docs/EXACT_SCOPE_ATTESTATION_SPEC.md +44 -0
  27. jring_client-0.5.0/docs/EXTERNAL_JRING_PRIOR_ART.md +35 -0
  28. jring_client-0.5.0/docs/INSTALL.md +352 -0
  29. jring_client-0.5.0/docs/JTBD.md +504 -0
  30. jring_client-0.5.0/docs/LICENSE_DECISION.md +83 -0
  31. jring_client-0.5.0/docs/OWNER_HARDWARE_EVIDENCE.md +224 -0
  32. jring_client-0.5.0/docs/PRIVATE_OBSERVATION_SPEC.md +68 -0
  33. jring_client-0.5.0/docs/PROTOCOL.md +1156 -0
  34. jring_client-0.5.0/docs/PUBLISHING.md +140 -0
  35. jring_client-0.5.0/docs/ROADMAP.md +125 -0
  36. jring_client-0.5.0/docs/UX_SPEC.md +1091 -0
  37. jring_client-0.5.0/docs/UX_TEST_MATRIX.md +74 -0
  38. jring_client-0.5.0/docs/VENDOR_AUTHORIZATION_GATES.md +140 -0
  39. jring_client-0.5.0/docs/VENDOR_GATT_TRANSACTION.md +110 -0
  40. jring_client-0.5.0/pyproject.toml +71 -0
  41. jring_client-0.5.0/requirements/release.txt +5 -0
  42. jring_client-0.5.0/scripts/__init__.py +1 -0
  43. jring_client-0.5.0/scripts/compatibility_matrix.py +280 -0
  44. jring_client-0.5.0/scripts/evidence_tool.py +1442 -0
  45. jring_client-0.5.0/scripts/generate_cli_artifacts.py +497 -0
  46. jring_client-0.5.0/scripts/release_artifacts.py +278 -0
  47. jring_client-0.5.0/setup.cfg +4 -0
  48. jring_client-0.5.0/src/jring/__init__.py +3 -0
  49. jring_client-0.5.0/src/jring/_vendor_binder_rows.py +646 -0
  50. jring_client-0.5.0/src/jring/_vendor_decode.py +14 -0
  51. jring_client-0.5.0/src/jring/bleak_transport.py +873 -0
  52. jring_client-0.5.0/src/jring/bluetooth_parity_manifest.py +133 -0
  53. jring_client-0.5.0/src/jring/clean_room_gap_registry.py +59 -0
  54. jring_client-0.5.0/src/jring/cli.py +3145 -0
  55. jring_client-0.5.0/src/jring/client.py +1074 -0
  56. jring_client-0.5.0/src/jring/diagnostics.py +11 -0
  57. jring_client-0.5.0/src/jring/discovery.py +92 -0
  58. jring_client-0.5.0/src/jring/errors.py +2 -0
  59. jring_client-0.5.0/src/jring/event_contracts.py +1008 -0
  60. jring_client-0.5.0/src/jring/input.py +315 -0
  61. jring_client-0.5.0/src/jring/non_health.py +474 -0
  62. jring_client-0.5.0/src/jring/owner_hardware_evidence.py +1760 -0
  63. jring_client-0.5.0/src/jring/private_observation.py +480 -0
  64. jring_client-0.5.0/src/jring/protocol.py +122 -0
  65. jring_client-0.5.0/src/jring/readiness.py +436 -0
  66. jring_client-0.5.0/src/jring/resources/completions/jring.bash +396 -0
  67. jring_client-0.5.0/src/jring/resources/man/jring.1 +435 -0
  68. jring_client-0.5.0/src/jring/transport.py +429 -0
  69. jring_client-0.5.0/src/jring/uuids.py +76 -0
  70. jring_client-0.5.0/src/jring/vendor_alarm_runtime_simulator.py +766 -0
  71. jring_client-0.5.0/src/jring/vendor_app_use_evidence.py +419 -0
  72. jring_client-0.5.0/src/jring/vendor_artifact_evidence.py +1023 -0
  73. jring_client-0.5.0/src/jring/vendor_authorization_gate.py +580 -0
  74. jring_client-0.5.0/src/jring/vendor_behavior_settings.py +457 -0
  75. jring_client-0.5.0/src/jring/vendor_binder_evidence.py +242 -0
  76. jring_client-0.5.0/src/jring/vendor_callback_event_registry.py +63 -0
  77. jring_client-0.5.0/src/jring/vendor_callback_surfaces.py +410 -0
  78. jring_client-0.5.0/src/jring/vendor_codec_registry.py +428 -0
  79. jring_client-0.5.0/src/jring/vendor_commands.py +509 -0
  80. jring_client-0.5.0/src/jring/vendor_coverage.py +698 -0
  81. jring_client-0.5.0/src/jring/vendor_decompilation_evidence.py +284 -0
  82. jring_client-0.5.0/src/jring/vendor_dispatcher_evidence.py +203 -0
  83. jring_client-0.5.0/src/jring/vendor_ecg_history_runtime_simulator.py +430 -0
  84. jring_client-0.5.0/src/jring/vendor_event_engine.py +31 -0
  85. jring_client-0.5.0/src/jring/vendor_gatt_preflight.py +403 -0
  86. jring_client-0.5.0/src/jring/vendor_gatt_transaction.py +1356 -0
  87. jring_client-0.5.0/src/jring/vendor_generic_history_runtime_simulator.py +492 -0
  88. jring_client-0.5.0/src/jring/vendor_history.py +630 -0
  89. jring_client-0.5.0/src/jring/vendor_history_runtime_simulator.py +477 -0
  90. jring_client-0.5.0/src/jring/vendor_input_preview.py +89 -0
  91. jring_client-0.5.0/src/jring/vendor_local_operations.py +412 -0
  92. jring_client-0.5.0/src/jring/vendor_main_commands.py +324 -0
  93. jring_client-0.5.0/src/jring/vendor_main_event_runtime_simulator.py +1096 -0
  94. jring_client-0.5.0/src/jring/vendor_notify.py +347 -0
  95. jring_client-0.5.0/src/jring/vendor_notify_runtime_simulator.py +930 -0
  96. jring_client-0.5.0/src/jring/vendor_operation_registry.py +484 -0
  97. jring_client-0.5.0/src/jring/vendor_ota_evidence.py +756 -0
  98. jring_client-0.5.0/src/jring/vendor_personal_settings.py +306 -0
  99. jring_client-0.5.0/src/jring/vendor_phone_integration.py +567 -0
  100. jring_client-0.5.0/src/jring/vendor_phone_volume_runtime_simulator.py +494 -0
  101. jring_client-0.5.0/src/jring/vendor_platform_surface.py +178 -0
  102. jring_client-0.5.0/src/jring/vendor_prior_art_candidate_codecs.py +71 -0
  103. jring_client-0.5.0/src/jring/vendor_prior_art_hypotheses.py +48 -0
  104. jring_client-0.5.0/src/jring/vendor_protocol.py +1377 -0
  105. jring_client-0.5.0/src/jring/vendor_raw_protocol.py +380 -0
  106. jring_client-0.5.0/src/jring/vendor_raw_runtime_simulator.py +399 -0
  107. jring_client-0.5.0/src/jring/vendor_request_builder_evidence.py +201 -0
  108. jring_client-0.5.0/src/jring/vendor_request_callback_correlation.py +698 -0
  109. jring_client-0.5.0/src/jring/vendor_request_integrity.py +74 -0
  110. jring_client-0.5.0/src/jring/vendor_request_routing.py +224 -0
  111. jring_client-0.5.0/src/jring/vendor_runtime_eligibility.py +201 -0
  112. jring_client-0.5.0/src/jring/vendor_runtime_fake.py +664 -0
  113. jring_client-0.5.0/src/jring/vendor_runtime_scope_eligibility.py +276 -0
  114. jring_client-0.5.0/src/jring/vendor_runtime_simulator.py +716 -0
  115. jring_client-0.5.0/src/jring/vendor_session_arbiter.py +106 -0
  116. jring_client-0.5.0/src/jring/vendor_session_evidence.py +480 -0
  117. jring_client-0.5.0/src/jring/vendor_settings.py +380 -0
  118. jring_client-0.5.0/src/jring/vendor_transport.py +1330 -0
  119. jring_client-0.5.0/src/jring/vendor_warning_evidence.py +471 -0
  120. jring_client-0.5.0/src/jring/vendor_wifi_runtime_simulator.py +606 -0
  121. jring_client-0.5.0/src/jring_client.egg-info/PKG-INFO +670 -0
  122. jring_client-0.5.0/src/jring_client.egg-info/SOURCES.txt +206 -0
  123. jring_client-0.5.0/src/jring_client.egg-info/dependency_links.txt +1 -0
  124. jring_client-0.5.0/src/jring_client.egg-info/entry_points.txt +3 -0
  125. jring_client-0.5.0/src/jring_client.egg-info/requires.txt +13 -0
  126. jring_client-0.5.0/src/jring_client.egg-info/top_level.txt +1 -0
  127. jring_client-0.5.0/tests/fixtures/compatibility/synthetic-python310.json +29 -0
  128. jring_client-0.5.0/tests/fixtures/compatibility/synthetic-python313.json +29 -0
  129. jring_client-0.5.0/tests/fixtures/evidence/synthetic-hid-fixture.json +20 -0
  130. jring_client-0.5.0/tests/fixtures/evidence/synthetic-hid-manifest.json +34 -0
  131. jring_client-0.5.0/tests/fixtures/evidence/synthetic-vendor-device-info-claim.json +91 -0
  132. jring_client-0.5.0/tests/fixtures/evidence/synthetic-vendor-device-info-fixture.json +75 -0
  133. jring_client-0.5.0/tests/test_bleak_transport.py +1221 -0
  134. jring_client-0.5.0/tests/test_bluetooth_parity_manifest.py +47 -0
  135. jring_client-0.5.0/tests/test_capabilities.py +891 -0
  136. jring_client-0.5.0/tests/test_clean_room_gap_registry.py +26 -0
  137. jring_client-0.5.0/tests/test_cli.py +2222 -0
  138. jring_client-0.5.0/tests/test_cli_artifacts.py +409 -0
  139. jring_client-0.5.0/tests/test_client.py +224 -0
  140. jring_client-0.5.0/tests/test_compatibility.py +111 -0
  141. jring_client-0.5.0/tests/test_event_contracts.py +736 -0
  142. jring_client-0.5.0/tests/test_evidence.py +1352 -0
  143. jring_client-0.5.0/tests/test_hardware.py +13 -0
  144. jring_client-0.5.0/tests/test_heart_rate_runtime.py +305 -0
  145. jring_client-0.5.0/tests/test_input.py +239 -0
  146. jring_client-0.5.0/tests/test_install_portability.py +135 -0
  147. jring_client-0.5.0/tests/test_license.py +33 -0
  148. jring_client-0.5.0/tests/test_non_health.py +369 -0
  149. jring_client-0.5.0/tests/test_owner_hardware_evidence_adversarial.py +1002 -0
  150. jring_client-0.5.0/tests/test_owner_hardware_evidence_runner.py +882 -0
  151. jring_client-0.5.0/tests/test_package_metadata.py +248 -0
  152. jring_client-0.5.0/tests/test_private_observation.py +312 -0
  153. jring_client-0.5.0/tests/test_protocol.py +95 -0
  154. jring_client-0.5.0/tests/test_publishing_workflow.py +151 -0
  155. jring_client-0.5.0/tests/test_readiness.py +238 -0
  156. jring_client-0.5.0/tests/test_release.py +243 -0
  157. jring_client-0.5.0/tests/test_safety.py +53 -0
  158. jring_client-0.5.0/tests/test_scope_keyed_runtime_eligibility.py +131 -0
  159. jring_client-0.5.0/tests/test_symphony_workflow.py +63 -0
  160. jring_client-0.5.0/tests/test_vendor_alarm_runtime_simulator.py +688 -0
  161. jring_client-0.5.0/tests/test_vendor_app_use_evidence.py +144 -0
  162. jring_client-0.5.0/tests/test_vendor_artifact_evidence.py +510 -0
  163. jring_client-0.5.0/tests/test_vendor_authorization_gate.py +439 -0
  164. jring_client-0.5.0/tests/test_vendor_behavior_settings.py +284 -0
  165. jring_client-0.5.0/tests/test_vendor_binder_evidence.py +171 -0
  166. jring_client-0.5.0/tests/test_vendor_callback_event_registry.py +29 -0
  167. jring_client-0.5.0/tests/test_vendor_callback_surfaces.py +194 -0
  168. jring_client-0.5.0/tests/test_vendor_codec_registry.py +136 -0
  169. jring_client-0.5.0/tests/test_vendor_commands.py +402 -0
  170. jring_client-0.5.0/tests/test_vendor_coverage.py +483 -0
  171. jring_client-0.5.0/tests/test_vendor_decompilation_evidence.py +186 -0
  172. jring_client-0.5.0/tests/test_vendor_dispatcher_evidence.py +85 -0
  173. jring_client-0.5.0/tests/test_vendor_ecg_history_runtime_simulator.py +427 -0
  174. jring_client-0.5.0/tests/test_vendor_event_engine.py +18 -0
  175. jring_client-0.5.0/tests/test_vendor_gatt_preflight.py +480 -0
  176. jring_client-0.5.0/tests/test_vendor_gatt_transaction.py +683 -0
  177. jring_client-0.5.0/tests/test_vendor_generic_history_runtime_simulator.py +450 -0
  178. jring_client-0.5.0/tests/test_vendor_history.py +460 -0
  179. jring_client-0.5.0/tests/test_vendor_history_runtime_simulator.py +388 -0
  180. jring_client-0.5.0/tests/test_vendor_input_preview.py +91 -0
  181. jring_client-0.5.0/tests/test_vendor_local_operations.py +249 -0
  182. jring_client-0.5.0/tests/test_vendor_main_commands.py +185 -0
  183. jring_client-0.5.0/tests/test_vendor_main_event_runtime_simulator.py +1352 -0
  184. jring_client-0.5.0/tests/test_vendor_notify.py +267 -0
  185. jring_client-0.5.0/tests/test_vendor_notify_runtime_simulator.py +757 -0
  186. jring_client-0.5.0/tests/test_vendor_operation_registry.py +188 -0
  187. jring_client-0.5.0/tests/test_vendor_ota_evidence.py +310 -0
  188. jring_client-0.5.0/tests/test_vendor_personal_settings.py +282 -0
  189. jring_client-0.5.0/tests/test_vendor_phone_integration.py +278 -0
  190. jring_client-0.5.0/tests/test_vendor_phone_volume_runtime_simulator.py +313 -0
  191. jring_client-0.5.0/tests/test_vendor_platform_surface.py +118 -0
  192. jring_client-0.5.0/tests/test_vendor_prior_art_candidate_codecs.py +29 -0
  193. jring_client-0.5.0/tests/test_vendor_prior_art_hypotheses.py +23 -0
  194. jring_client-0.5.0/tests/test_vendor_protocol.py +1166 -0
  195. jring_client-0.5.0/tests/test_vendor_raw_protocol.py +303 -0
  196. jring_client-0.5.0/tests/test_vendor_raw_runtime_simulator.py +298 -0
  197. jring_client-0.5.0/tests/test_vendor_request_builder_evidence.py +162 -0
  198. jring_client-0.5.0/tests/test_vendor_request_callback_correlation.py +710 -0
  199. jring_client-0.5.0/tests/test_vendor_request_routing.py +100 -0
  200. jring_client-0.5.0/tests/test_vendor_runtime_eligibility.py +135 -0
  201. jring_client-0.5.0/tests/test_vendor_runtime_fake.py +555 -0
  202. jring_client-0.5.0/tests/test_vendor_runtime_simulator.py +857 -0
  203. jring_client-0.5.0/tests/test_vendor_session_arbiter.py +35 -0
  204. jring_client-0.5.0/tests/test_vendor_session_evidence.py +222 -0
  205. jring_client-0.5.0/tests/test_vendor_settings.py +283 -0
  206. jring_client-0.5.0/tests/test_vendor_transport.py +1420 -0
  207. jring_client-0.5.0/tests/test_vendor_warning_evidence.py +210 -0
  208. jring_client-0.5.0/tests/test_vendor_wifi_runtime_simulator.py +645 -0
@@ -0,0 +1,98 @@
1
+ name: Native container smoke
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ distro-smoke:
12
+ runs-on: ubuntu-24.04
13
+ timeout-minutes: 25
14
+ container:
15
+ image: ${{ matrix.image }}
16
+ strategy:
17
+ fail-fast: false
18
+ max-parallel: 2
19
+ matrix:
20
+ include:
21
+ - family: debian
22
+ image: debian:13-slim
23
+ python_command: python3
24
+ - family: fedora
25
+ image: fedora:44
26
+ python_command: python3
27
+ - family: enterprise-linux-9
28
+ # Follow Rocky's supported EL9 minor; retired minor repos are unsuitable CI inputs.
29
+ image: rockylinux/rockylinux:9
30
+ python_command: python3.11
31
+ # Arch is a rolling distribution and does not publish fixed release tags.
32
+ - family: arch
33
+ image: archlinux:base
34
+ python_command: python
35
+ - family: opensuse
36
+ image: opensuse/leap:15.6
37
+ python_command: python3.11
38
+ env:
39
+ JRING_HARDWARE_TEST: "0"
40
+ PIP_DISABLE_PIP_VERSION_CHECK: "1"
41
+ steps:
42
+ - name: Install ephemeral native prerequisites
43
+ shell: sh
44
+ run: |
45
+ case "${{ matrix.family }}" in
46
+ debian)
47
+ export DEBIAN_FRONTEND=noninteractive
48
+ apt-get update
49
+ apt-get install -y --no-install-recommends \
50
+ git ca-certificates python3 python3-venv python3-pip python3-dev \
51
+ build-essential linux-libc-dev bluez
52
+ ;;
53
+ fedora)
54
+ dnf install -y \
55
+ git ca-certificates python3 python3-pip python3-devel gcc \
56
+ kernel-headers bluez
57
+ ;;
58
+ enterprise-linux-9)
59
+ dnf install -y \
60
+ git ca-certificates python3.11 python3.11-pip python3.11-devel \
61
+ gcc kernel-headers bluez
62
+ ;;
63
+ arch)
64
+ pacman -Syu --noconfirm --needed \
65
+ git ca-certificates python python-pip base-devel linux-api-headers \
66
+ bluez bluez-utils
67
+ ;;
68
+ opensuse)
69
+ zypper --non-interactive install --no-recommends \
70
+ git ca-certificates python311 python311-pip python311-devel gcc make \
71
+ linux-glibc-devel bluez
72
+ ;;
73
+ *)
74
+ echo "unsupported smoke family" >&2
75
+ exit 2
76
+ ;;
77
+ esac
78
+
79
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
80
+
81
+ - name: Create isolated environment and install all extras
82
+ shell: sh
83
+ run: |
84
+ "${{ matrix.python_command }}" --version
85
+ "${{ matrix.python_command }}" -m venv /tmp/jring-smoke-venv
86
+ . /tmp/jring-smoke-venv/bin/activate
87
+ python -m pip install --upgrade pip
88
+ python -m pip install -e '.[ble,input,dev]'
89
+
90
+ - name: Run non-hardware tests and passive simulator smoke
91
+ shell: sh
92
+ run: |
93
+ . /tmp/jring-smoke-venv/bin/activate
94
+ python -m pytest -q
95
+ jring doctor --json
96
+ jring status --simulate --json
97
+ jring capabilities --simulate --json
98
+ jring input --simulate --map step=key:space
@@ -0,0 +1,120 @@
1
+ name: publish-pypi
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ publish:
7
+ description: Publish the validated artifacts to PyPI after environment approval
8
+ required: true
9
+ type: boolean
10
+ default: false
11
+ push:
12
+ tags: ["v*"]
13
+
14
+ permissions: {}
15
+
16
+ concurrency:
17
+ group: publish-pypi-${{ github.ref }}
18
+ cancel-in-progress: false
19
+
20
+ jobs:
21
+ build-and-validate:
22
+ runs-on: ubuntu-latest
23
+ permissions:
24
+ contents: read
25
+ outputs:
26
+ artifact-id: ${{ steps.upload.outputs.artifact-id }}
27
+ steps:
28
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
29
+ with:
30
+ persist-credentials: false
31
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
32
+ with:
33
+ python-version: "3.13"
34
+ cache: pip
35
+ - name: Install pinned build tools
36
+ run: python -m pip install -r requirements/release.txt
37
+ - name: Validate source and publishing intent
38
+ shell: bash
39
+ env:
40
+ PUBLISH_REQUESTED: ${{ (github.event_name == 'push' && github.ref_type == 'tag') || inputs.publish }}
41
+ run: |
42
+ set -euo pipefail
43
+ git diff --exit-code
44
+ python scripts/evidence_tool.py scan .
45
+ if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
46
+ python scripts/release_artifacts.py check-version "${GITHUB_REF_NAME}"
47
+ elif [[ "${PUBLISH_REQUESTED}" == "true" ]]; then
48
+ echo "Publishing requires selecting the matching protected version tag." >&2
49
+ exit 2
50
+ fi
51
+ python scripts/generate_cli_artifacts.py --check
52
+ if [[ "${PUBLISH_REQUESTED}" == "true" && "${GITHUB_REF_PROTECTED}" != "true" ]]; then
53
+ echo "Publishing requires a protected tag." >&2
54
+ exit 2
55
+ fi
56
+ - name: Build twice and validate reproducibility
57
+ shell: bash
58
+ run: |
59
+ set -euo pipefail
60
+ export SOURCE_DATE_EPOCH="$(git show -s --format=%ct HEAD)"
61
+ python -m build --no-isolation --outdir dist-a
62
+ python -m build --no-isolation --outdir dist-b
63
+ python scripts/release_artifacts.py normalize-sdist dist-a/*.tar.gz --epoch "${SOURCE_DATE_EPOCH}"
64
+ python scripts/release_artifacts.py normalize-sdist dist-b/*.tar.gz --epoch "${SOURCE_DATE_EPOCH}"
65
+ python scripts/release_artifacts.py compare dist-a dist-b
66
+ python scripts/release_artifacts.py inspect dist-a
67
+ mkdir dist
68
+ cp dist-a/*.whl dist-a/*.tar.gz dist/
69
+ - name: Install exact wheel and smoke offline paths
70
+ shell: bash
71
+ run: |
72
+ set -euo pipefail
73
+ python -m venv /tmp/jring-publish-smoke
74
+ /tmp/jring-publish-smoke/bin/python -m pip install --no-index --no-deps dist/*.whl
75
+ cd /tmp
76
+ /tmp/jring-publish-smoke/bin/jring doctor --json
77
+ /tmp/jring-publish-smoke/bin/jring status --simulate --json
78
+ /tmp/jring-publish-smoke/bin/jring capabilities --simulate --json
79
+ - name: Store the exact validated distributions
80
+ id: upload
81
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
82
+ with:
83
+ name: pypi-distributions-${{ github.sha }}
84
+ path: |
85
+ dist/*.whl
86
+ dist/*.tar.gz
87
+ if-no-files-found: error
88
+ retention-days: 2
89
+
90
+ publish-pypi:
91
+ needs: build-and-validate
92
+ if: >-
93
+ (
94
+ github.event_name == 'push' &&
95
+ github.ref_type == 'tag' &&
96
+ github.ref_protected == true &&
97
+ startsWith(github.ref_name, 'v')
98
+ ) || (
99
+ inputs.publish == true &&
100
+ github.ref_type == 'tag' &&
101
+ github.ref_protected == true &&
102
+ startsWith(github.ref_name, 'v')
103
+ )
104
+ runs-on: ubuntu-latest
105
+ environment:
106
+ name: pypi
107
+ url: https://pypi.org/project/jring-client/
108
+ permissions:
109
+ id-token: write
110
+ steps:
111
+ - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
112
+ with:
113
+ artifact-ids: ${{ needs.build-and-validate.outputs.artifact-id }}
114
+ path: dist
115
+ - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
116
+ with:
117
+ packages-dir: dist
118
+ attestations: true
119
+ print-hash: true
120
+ verbose: false
@@ -0,0 +1,104 @@
1
+ name: release-artifacts
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ push:
6
+ tags: ["v*"]
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ build-and-verify:
13
+ runs-on: ubuntu-latest
14
+ permissions:
15
+ contents: read
16
+ id-token: write
17
+ attestations: write
18
+ steps:
19
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
20
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
21
+ with:
22
+ python-version: "3.13"
23
+ cache: pip
24
+ - name: Install pinned build tools
25
+ shell: bash
26
+ run: |
27
+ python -m pip download --only-binary=:all: \
28
+ --dest /tmp/jring-build-wheelhouse -r requirements/release.txt
29
+ python -m pip install -r requirements/release.txt
30
+ - name: Require a clean source and matching tag
31
+ shell: bash
32
+ run: |
33
+ git diff --exit-code
34
+ python scripts/evidence_tool.py scan .
35
+ if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
36
+ python scripts/release_artifacts.py check-version "${GITHUB_REF_NAME}"
37
+ fi
38
+ python scripts/generate_cli_artifacts.py --check
39
+ - name: Prove isolated no-index build from pinned inputs
40
+ shell: bash
41
+ run: |
42
+ mkdir /tmp/jring-offline-source /tmp/jring-offline-dist
43
+ git archive HEAD -o /tmp/jring-offline-source.tar
44
+ tar -xf /tmp/jring-offline-source.tar -C /tmp/jring-offline-source
45
+ python -m venv /tmp/jring-offline-build
46
+ /tmp/jring-offline-build/bin/python -m pip install --no-index --find-links \
47
+ /tmp/jring-build-wheelhouse -r requirements/release.txt
48
+ cd /tmp/jring-offline-source
49
+ PIP_NO_INDEX=1 PIP_FIND_LINKS=/tmp/jring-build-wheelhouse \
50
+ /tmp/jring-offline-build/bin/python -m build \
51
+ --outdir /tmp/jring-offline-dist
52
+ /tmp/jring-offline-build/bin/python scripts/release_artifacts.py \
53
+ inspect /tmp/jring-offline-dist
54
+ - name: Build twice and compare bytes
55
+ shell: bash
56
+ run: |
57
+ export SOURCE_DATE_EPOCH="$(git show -s --format=%ct HEAD)"
58
+ python -m build --no-isolation --outdir dist-a
59
+ python -m build --no-isolation --outdir dist-b
60
+ python scripts/release_artifacts.py normalize-sdist dist-a/*.tar.gz --epoch "${SOURCE_DATE_EPOCH}"
61
+ python scripts/release_artifacts.py normalize-sdist dist-b/*.tar.gz --epoch "${SOURCE_DATE_EPOCH}"
62
+ python scripts/release_artifacts.py compare dist-a dist-b
63
+ python scripts/release_artifacts.py inspect dist-a
64
+ mkdir dist
65
+ cp dist-a/* dist/
66
+ python scripts/release_artifacts.py checksums dist
67
+ - name: Attest artifact provenance
68
+ uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
69
+ with:
70
+ subject-path: |
71
+ dist/*.whl
72
+ dist/*.tar.gz
73
+ dist/SHA256SUMS
74
+ - name: Upload reviewed artifacts without publishing a release
75
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
76
+ with:
77
+ name: jring-client-${{ github.sha }}
78
+ path: dist/
79
+ if-no-files-found: error
80
+ retention-days: 14
81
+
82
+ clean-install-smoke:
83
+ needs: build-and-verify
84
+ runs-on: ubuntu-latest
85
+ strategy:
86
+ matrix:
87
+ python-version: ["3.10", "3.13"]
88
+ steps:
89
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
90
+ with:
91
+ python-version: ${{ matrix.python-version }}
92
+ - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
93
+ with:
94
+ name: jring-client-${{ github.sha }}
95
+ path: dist
96
+ - name: Install outside the checkout and smoke safe paths
97
+ shell: bash
98
+ run: |
99
+ python -m venv /tmp/jring-release-smoke
100
+ /tmp/jring-release-smoke/bin/python -m pip install dist/*.whl
101
+ cd /tmp
102
+ /tmp/jring-release-smoke/bin/jring doctor --json
103
+ /tmp/jring-release-smoke/bin/jring status --simulate --json
104
+ /tmp/jring-release-smoke/bin/jring capabilities --simulate --json
@@ -0,0 +1,24 @@
1
+ name: test
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ test:
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ matrix:
15
+ python-version: ["3.10", "3.13"]
16
+ steps:
17
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
18
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
19
+ with:
20
+ python-version: ${{ matrix.python-version }}
21
+ cache: pip
22
+ - run: python -m pip install -e '.[ble,dev]'
23
+ - run: python scripts/generate_cli_artifacts.py --check
24
+ - run: python -m pytest -q
@@ -0,0 +1,42 @@
1
+ name: uv tool smoke
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ uv-tool-smoke:
13
+ runs-on: ubuntu-latest
14
+ timeout-minutes: 15
15
+ steps:
16
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
17
+ with:
18
+ persist-credentials: false
19
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
20
+ with:
21
+ python-version: "3.13"
22
+ cache: pip
23
+ - uses: astral-sh/setup-uv@d08d816a1ea176d61a318eff45abd3dffef415b1 # main, resolved 2026-08-26
24
+ with:
25
+ enable-cache: false
26
+ - name: Build the reviewed wheel
27
+ shell: bash
28
+ run: |
29
+ set -euo pipefail
30
+ python -m pip install -r requirements/release.txt
31
+ python scripts/evidence_tool.py scan .
32
+ python scripts/generate_cli_artifacts.py --check
33
+ python -m build --wheel --outdir dist
34
+ - name: Install the exact wheel as an isolated uv tool
35
+ shell: bash
36
+ run: |
37
+ set -euo pipefail
38
+ uv tool install dist/*.whl
39
+ jring --version
40
+ jring status --simulate --json
41
+ jring capabilities --simulate --json
42
+ jring-tui </dev/null
@@ -0,0 +1,55 @@
1
+ # Contributing to JRing client
2
+
3
+ Start with a public issue describing the user job, intended behavior, executable tests,
4
+ artifacts, and blockers. Keep each change independently reviewable. Update JTBD and UX
5
+ specification text before implementation, add a failing test, then make it pass.
6
+
7
+ ## Evidence safety
8
+
9
+ Never attach or commit raw Bluetooth captures, Android application archives, addresses,
10
+ BlueZ object paths, device or account identifiers, precise personal timestamps, health
11
+ measurements, report maps, or vendor payload dumps. Do not paste them into an issue,
12
+ pull request, CI log, commit message, or chat transcript.
13
+
14
+ Work with synthetic evidence unless the owner separately authorizes a local hardware
15
+ observation. Keep originals outside the repository. Create a minimal manifest from the
16
+ synthetic example, then validate and derive locally:
17
+
18
+ ```sh
19
+ python3 scripts/evidence_tool.py validate path/to/manifest.json
20
+ python3 scripts/evidence_tool.py derive path/to/manifest.json > /tmp/jring-fixture.json
21
+ ```
22
+
23
+ The validator refuses unsafe content; it does not promise to anonymize it. Review the
24
+ derived fixture yourself before adding it. Run `python3 scripts/evidence_tool.py scan .`
25
+ and the full tests before committing. If a finding or reproduction needs sensitive
26
+ material, stop and follow [SECURITY.md](SECURITY.md) instead of opening a public issue.
27
+
28
+ Schema-2 `*-claim.json` files are public review candidates and must have an exact
29
+ `*-fixture.json` derivation. The current allowlist contains only the sealed vendor-main
30
+ device-info canary shape. Do not turn a private owner manifest into a tracked claim:
31
+ construct and review the minimal public candidate separately. Schema validation grants no
32
+ permission to connect, activate notifications, write, publish private evidence, or
33
+ claim hardware support.
34
+
35
+ A schema-2 private device-info observation is a self-declared record of one historical
36
+ attempt only. It withholds identifiers and model/firmware context. Keep it
37
+ outside the repository, owned by the current user, and mode 0600 (or read-only 0400),
38
+ then run only `python3 scripts/evidence_tool.py validate path/to/observation.json`.
39
+ Validation performs no Bluetooth operation and authenticates neither the owner nor the
40
+ observation. `derive` deliberately refuses this artifact; do not commit, attach, paste,
41
+ or publish it. A future pre-run plan, its one attempt, this post-run observation, and
42
+ any separately consented public candidate are four different artifacts and decisions.
43
+
44
+ The `-manifest.json`, `-claim.json`, and `-fixture.json` suffixes are reserved across
45
+ the repository; moving an unpaired artifact outside `tests/fixtures/evidence` does not
46
+ bypass validation.
47
+
48
+ Vendor writes remain disabled. Evidence contribution never authorizes scanning,
49
+ connecting, subscribing, input injection, or packet emission.
50
+
51
+ ## License
52
+
53
+ Project source, including accepted contributions, is distributed under the
54
+ [MIT License](LICENSE). This repository does not add a separate contributor license
55
+ agreement.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 JRing Client contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,10 @@
1
+ include CONTRIBUTING.md
2
+ include LICENSE
3
+ include SECURITY.md
4
+ recursive-include .github/workflows *.yml
5
+ recursive-include docs *.md
6
+ recursive-include requirements *.txt
7
+ recursive-include scripts *.py
8
+ include src/jring/resources/completions/jring.bash
9
+ include src/jring/resources/man/jring.1
10
+ recursive-include tests/fixtures *.json