skcomms 0.2.2__tar.gz → 0.2.4__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 (413) hide show
  1. skcomms-0.2.4/.github/workflows/ci.yml +99 -0
  2. skcomms-0.2.4/.github/workflows/publish.yml +134 -0
  3. skcomms-0.2.4/.github/workflows/pytest.yml +127 -0
  4. skcomms-0.2.4/.github/workflows/qa.yml +55 -0
  5. skcomms-0.2.4/.github/workflows/secret-scan.yml +48 -0
  6. skcomms-0.2.4/.gitignore +16 -0
  7. skcomms-0.2.4/.gitleaks.toml +35 -0
  8. skcomms-0.2.4/CHANGELOG.md +243 -0
  9. skcomms-0.2.4/CODE_OF_CONDUCT.md +129 -0
  10. skcomms-0.2.4/CONTRIBUTING.md +162 -0
  11. skcomms-0.2.4/DEPLOY-NOTES.md +105 -0
  12. skcomms-0.2.4/Dockerfile +47 -0
  13. skcomms-0.2.4/MISSION.md +19 -0
  14. {skcomms-0.2.2 → skcomms-0.2.4}/PKG-INFO +1 -1
  15. skcomms-0.2.4/SECURITY.md +152 -0
  16. skcomms-0.2.4/SOP.md +625 -0
  17. skcomms-0.2.4/constraints.txt +89 -0
  18. skcomms-0.2.4/contrib/systemd/skcomms-api.service +50 -0
  19. skcomms-0.2.4/contrib/systemd/skcomms-housekeep.service +24 -0
  20. skcomms-0.2.4/contrib/systemd/skcomms-housekeep.timer +19 -0
  21. skcomms-0.2.4/docs/ARCHITECTURE.md +370 -0
  22. skcomms-0.2.4/docs/DISCORD_SETUP.md +185 -0
  23. skcomms-0.2.4/docs/HANDOFF-skfed-s2s-opus-delivery.md +80 -0
  24. skcomms-0.2.4/docs/PAIRING.md +187 -0
  25. skcomms-0.2.4/docs/REVOCATION_LATENCY.md +175 -0
  26. skcomms-0.2.4/docs/SYNCTHING_TOPOLOGY.md +470 -0
  27. skcomms-0.2.4/docs/access-plane-p7.md +201 -0
  28. skcomms-0.2.4/docs/access-plane-p8.md +119 -0
  29. skcomms-0.2.4/docs/cot-bridge.md +130 -0
  30. skcomms-0.2.4/docs/crypto-architecture.md +227 -0
  31. skcomms-0.2.4/docs/deploy-plan/skcomms-bulletproof-deploy.md +196 -0
  32. skcomms-0.2.4/docs/federation-data-comms-architecture.md +325 -0
  33. skcomms-0.2.4/docs/funnel-privacy.md +195 -0
  34. skcomms-0.2.4/docs/mode-b-tailnet-deploy.md +56 -0
  35. skcomms-0.2.4/docs/node_registry.example.yml +36 -0
  36. skcomms-0.2.4/docs/rfcs/RFC-0001-pq-ratchet-metadata-dual-identity.md +339 -0
  37. skcomms-0.2.4/docs/skfed-consent-design.md +329 -0
  38. skcomms-0.2.4/docs/skfed-federation-guide.md +257 -0
  39. skcomms-0.2.4/docs/state-sync-rearch-plan.md +398 -0
  40. skcomms-0.2.4/docs/superpowers/plans/2026-06-10-qr-pairing.md +375 -0
  41. skcomms-0.2.4/docs/superpowers/plans/2026-06-13-ble-mesh-p1-core.md +1568 -0
  42. skcomms-0.2.4/docs/superpowers/plans/2026-06-13-lora-l1-core.md +783 -0
  43. skcomms-0.2.4/docs/superpowers/plans/2026-06-13-skglossa-g1-core.md +674 -0
  44. skcomms-0.2.4/docs/superpowers/plans/2026-06-13-skglossa-g2-macro-lexicon.md +381 -0
  45. skcomms-0.2.4/docs/superpowers/plans/2026-06-13-skglossa-g4-emergent.md +272 -0
  46. skcomms-0.2.4/docs/superpowers/plans/2026-06-13-tier3-adapter-runtime.md +187 -0
  47. skcomms-0.2.4/docs/superpowers/specs/2026-06-13-ble-mesh-proximity-transport-design.md +193 -0
  48. skcomms-0.2.4/docs/superpowers/specs/2026-06-13-lora-meshtastic-transport-design.md +158 -0
  49. skcomms-0.2.4/docs/superpowers/specs/2026-06-13-skglossa-design.md +335 -0
  50. skcomms-0.2.4/docs/tak-vs-skfed-analysis.md +52 -0
  51. {skcomms-0.2.2 → skcomms-0.2.4}/pyproject.toml +16 -2
  52. skcomms-0.2.4/scripts/backfill_file_locations.py +249 -0
  53. skcomms-0.2.4/scripts/bootstrap.sh +169 -0
  54. skcomms-0.2.4/scripts/discord_smoke.py +198 -0
  55. skcomms-0.2.4/scripts/drain_orphan_outbox.py +268 -0
  56. skcomms-0.2.4/scripts/mode-b-setup.sh +383 -0
  57. skcomms-0.2.4/scripts/refresh-constraints.sh +75 -0
  58. skcomms-0.2.4/scripts/skfed-dns.sh +117 -0
  59. skcomms-0.2.4/scripts/skfed-neutral-addresses.sh +73 -0
  60. skcomms-0.2.4/scripts/skfed-sync-rearch.sh +353 -0
  61. skcomms-0.2.4/scripts/telegram_smoke.py +245 -0
  62. skcomms-0.2.4/scripts/templates/consent-mode.conf.tmpl +7 -0
  63. skcomms-0.2.4/scripts/templates/realms.yml.tmpl +4 -0
  64. skcomms-0.2.4/scripts/templates/tailscale-serve.sh.tmpl +8 -0
  65. skcomms-0.2.4/scripts/test_skfed_sync_rearch.sh +84 -0
  66. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/__init__.py +10 -10
  67. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/access/__init__.py +4 -2
  68. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/access/config.py +3 -1
  69. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/access/exec.py +6 -7
  70. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/access/files.py +11 -7
  71. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/access/grants.py +3 -1
  72. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/access/knowledge.py +14 -14
  73. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/access/routing.py +51 -22
  74. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/access/server.py +55 -32
  75. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/access/wiring.py +14 -6
  76. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/ack.py +1 -3
  77. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/adapters/discord.py +14 -26
  78. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/adapters/matrix.py +22 -28
  79. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/adapters/registry.py +2 -6
  80. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/adapters/slack.py +19 -25
  81. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/adapters/telegram.py +14 -28
  82. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/anon_queue.py +3 -7
  83. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/anon_transport.py +14 -19
  84. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/api.py +74 -11
  85. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/capabilities.py +3 -4
  86. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/capauth_validator.py +7 -7
  87. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/cli.py +74 -41
  88. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/cluster.py +3 -9
  89. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/config.py +1 -3
  90. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/consent.py +7 -7
  91. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/consent_banfeeds.py +1 -2
  92. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/consent_captcha.py +6 -9
  93. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/consent_greylist.py +1 -0
  94. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/consent_group_gate.py +6 -4
  95. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/consent_groups.py +21 -16
  96. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/consent_moderation.py +3 -7
  97. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/consent_pipeline.py +2 -3
  98. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/consent_policy.py +5 -5
  99. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/consent_requests.py +2 -3
  100. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/consent_runtime.py +3 -6
  101. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/consent_tiering.py +8 -15
  102. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/consent_tokens.py +2 -3
  103. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/core.py +14 -22
  104. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/crypto.py +8 -5
  105. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/crypto_suites.py +4 -4
  106. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/did_router.py +3 -1
  107. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/envelope.py +4 -6
  108. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/federation.py +1 -3
  109. skcomms-0.2.4/src/skcomms/geo_seed.py +168 -0
  110. skcomms-0.2.4/src/skcomms/geo_store.py +232 -0
  111. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/glossa/codebook.py +16 -4
  112. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/glossa/codec.py +7 -6
  113. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/glossa/emergent.py +4 -3
  114. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/glossa/gloss.py +3 -2
  115. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/glossa/handshake.py +15 -10
  116. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/glossa/macros.py +5 -5
  117. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/glossa/message.py +9 -5
  118. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/glossa/session.py +7 -2
  119. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/grants.py +6 -14
  120. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/housekeeping.py +1 -3
  121. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/integration.py +1 -0
  122. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/key_exchange.py +4 -1
  123. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/mailbox.py +3 -1
  124. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/mcp_server.py +15 -5
  125. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/nostr_discovery.py +7 -15
  126. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/observability.py +2 -8
  127. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/operator_probe.py +26 -5
  128. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/outbox.py +12 -5
  129. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/outbox_migrate.py +2 -6
  130. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/padding.py +2 -5
  131. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/pairing.py +49 -18
  132. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/peers.py +3 -9
  133. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/pqdm.py +233 -11
  134. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/pqkem.py +4 -9
  135. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/pqroute.py +2 -10
  136. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/pqroute_transport.py +6 -6
  137. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/pqsig.py +15 -32
  138. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/public_pairing.py +1 -3
  139. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/registry.py +1 -1
  140. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/router.py +6 -11
  141. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/signing.py +4 -12
  142. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/skfed_announce.py +3 -1
  143. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/skfed_directory.py +1 -2
  144. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/skfed_readdr.py +8 -8
  145. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/skfed_resolve.py +3 -1
  146. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/ssrf.py +4 -12
  147. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/store_forward.py +3 -9
  148. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/syncthing_rail.py +4 -4
  149. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/tofu.py +2 -6
  150. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/ble/gatt.py +1 -1
  151. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/ble/identity.py +4 -4
  152. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/ble/node.py +11 -5
  153. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/ble/noise.py +7 -4
  154. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/ble/protocol.py +40 -17
  155. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/ble/relay.py +5 -7
  156. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/file.py +3 -10
  157. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/http_s2s.py +4 -9
  158. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/lora/addressing.py +1 -2
  159. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/lora/framing.py +2 -2
  160. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/lora/interface.py +2 -3
  161. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/lora/meshtastic_iface.py +15 -8
  162. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/lora/store.py +2 -1
  163. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/lora/transport.py +24 -12
  164. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/p2p_manager.py +1 -0
  165. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/p2p_session.py +1 -3
  166. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/signaling_broker.py +17 -11
  167. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/signaling_mailbox.py +13 -8
  168. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/webrtc.py +1 -3
  169. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/trustbackup.py +1 -7
  170. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms.egg-info/PKG-INFO +1 -1
  171. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms.egg-info/SOURCES.txt +80 -1
  172. skcomms-0.2.4/src/skcomms.egg-info/scm_file_list.json +407 -0
  173. skcomms-0.2.4/src/skcomms.egg-info/scm_version.json +8 -0
  174. skcomms-0.2.4/tests/__init__.py +0 -0
  175. skcomms-0.2.4/tests/conftest.py +69 -0
  176. skcomms-0.2.4/tests/fixtures/pqdm2_from_dart.json +18 -0
  177. skcomms-0.2.4/tests/gen_pqdm2_fixture.py +75 -0
  178. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_access_exec.py +2 -7
  179. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_access_files.py +1 -7
  180. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_access_nonce_durable.py +7 -6
  181. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_access_rbac.py +10 -5
  182. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_access_routing.py +30 -16
  183. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_access_server.py +10 -7
  184. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_access_wiring.py +12 -3
  185. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_ack_bind.py +1 -3
  186. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_adapter_factory.py +2 -2
  187. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_anon_queue.py +7 -7
  188. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_anon_transport.py +1 -1
  189. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_api_consent.py +0 -1
  190. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_api_consent_token_gate.py +0 -1
  191. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_api_cors.py +1 -4
  192. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_api_federation_inbox.py +12 -7
  193. skcomms-0.2.4/tests/test_api_geo_seed.py +119 -0
  194. skcomms-0.2.4/tests/test_api_geo_skcot_bridge.py +223 -0
  195. skcomms-0.2.4/tests/test_api_geo_units.py +144 -0
  196. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_api_send_signed_wire.py +35 -15
  197. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_api_skfed_directory.py +26 -12
  198. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_backfill_file_locations.py +0 -1
  199. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_ble_fragment.py +15 -5
  200. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_ble_identity.py +4 -2
  201. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_ble_noise.py +3 -3
  202. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_ble_protocol.py +1 -0
  203. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_ble_relay.py +7 -2
  204. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_broker_server.py +5 -2
  205. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_capabilities.py +4 -0
  206. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_capauth_failclosed.py +1 -3
  207. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_capauth_key_reconcile.py +1 -0
  208. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_capauth_resolution.py +2 -3
  209. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_channel_adapter.py +3 -9
  210. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_config.py +1 -2
  211. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_consent.py +2 -2
  212. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_consent_banfeeds.py +0 -1
  213. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_consent_captcha.py +1 -0
  214. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_consent_cli.py +1 -0
  215. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_consent_cli_mgmt.py +1 -0
  216. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_consent_greylist.py +3 -2
  217. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_consent_group_gate.py +10 -0
  218. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_consent_groups.py +8 -2
  219. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_consent_moderation.py +3 -0
  220. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_consent_pipeline.py +5 -5
  221. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_consent_policy.py +20 -18
  222. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_consent_requests.py +14 -0
  223. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_consent_runtime.py +1 -0
  224. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_consent_tiering.py +6 -7
  225. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_consent_tokens.py +1 -0
  226. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_core_inbound_beacon_skip.py +4 -9
  227. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_core_send_federated.py +50 -23
  228. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_core_send_signed.py +68 -39
  229. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_core_stop.py +1 -0
  230. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_crypto_suites_q0.py +13 -7
  231. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_delivery_semantics.py +1 -3
  232. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_discord_adapter.py +16 -18
  233. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_envelope_v1.py +0 -1
  234. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_federation.py +33 -19
  235. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_federation_integration.py +76 -28
  236. skcomms-0.2.4/tests/test_geo_roundtrip_skcot.py +139 -0
  237. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_glossa_codebook.py +3 -3
  238. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_glossa_codec_l0.py +4 -3
  239. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_glossa_codec_l1.py +7 -3
  240. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_glossa_codec_l2.py +11 -12
  241. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_glossa_emergent.py +4 -4
  242. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_glossa_emergent_negotiator.py +5 -4
  243. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_glossa_emergent_protocol.py +1 -0
  244. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_glossa_gloss.py +3 -2
  245. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_glossa_handshake.py +5 -4
  246. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_glossa_handshake_lexicon.py +8 -4
  247. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_glossa_macros.py +6 -2
  248. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_glossa_macros_render.py +6 -7
  249. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_glossa_message.py +1 -2
  250. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_glossa_session.py +9 -5
  251. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_glossa_session_macros.py +10 -6
  252. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_glossa_to_human.py +2 -4
  253. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_grants.py +8 -17
  254. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_home_scaffold.py +0 -1
  255. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_housekeeping.py +3 -9
  256. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_http_s2s_transport.py +6 -12
  257. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_identity_env_override.py +2 -4
  258. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_identity_key_binding.py +1 -1
  259. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_identity_realm.py +1 -4
  260. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_identity_trust_backup.py +5 -17
  261. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_integration.py +7 -2
  262. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_key_exchange_transport_scoping.py +2 -6
  263. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_knowledge.py +13 -13
  264. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_lora_addressing.py +1 -1
  265. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_lora_framing.py +9 -4
  266. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_lora_interface.py +2 -2
  267. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_lora_store.py +10 -10
  268. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_lora_transport.py +19 -6
  269. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_mailbox.py +8 -8
  270. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_mailbox_encrypt_at_rest.py +17 -24
  271. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_mailbox_recipient_key.py +1 -0
  272. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_matrix_adapter.py +19 -27
  273. skcomms-0.2.4/tests/test_mode_b_setup.sh +157 -0
  274. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_no_subapp_dependency.py +2 -4
  275. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_node_registry.py +1 -4
  276. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_nonce_durable.py +2 -5
  277. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_nostr_discovery.py +8 -4
  278. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_observability.py +2 -7
  279. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_operator_cli.py +20 -0
  280. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_outbox_home_alignment.py +2 -4
  281. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_outbox_migrate.py +2 -8
  282. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_p2p_connector.py +10 -2
  283. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_p2p_manager.py +15 -7
  284. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_p2p_media.py +12 -3
  285. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_p2p_session.py +9 -0
  286. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_padding.py +1 -1
  287. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_pairing.py +88 -31
  288. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_pairing_cli.py +7 -2
  289. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_pairing_mirror.py +45 -20
  290. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_peers.py +5 -13
  291. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_pqc_cutover.py +1 -1
  292. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_pqdm.py +9 -3
  293. skcomms-0.2.4/tests/test_pqdm2.py +132 -0
  294. skcomms-0.2.4/tests/test_pqdm2_interop.py +80 -0
  295. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_pqkem.py +3 -9
  296. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_pqroute_padding.py +25 -11
  297. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_pqroute_transport.py +17 -9
  298. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_pqsig.py +1 -3
  299. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_public_pairing.py +0 -1
  300. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_queue_bounds.py +1 -3
  301. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_receiver_wire_ttl_expiry.py +11 -5
  302. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_registry.py +2 -6
  303. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_registry_cli.py +2 -6
  304. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_router_failure_log_dedup.py +21 -14
  305. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_router_perm_backoff_fixes.py +7 -3
  306. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_router_quarantine.py +10 -3
  307. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_router_rail_ordering.py +1 -2
  308. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_router_route_anon.py +12 -10
  309. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_router_route_signed.py +30 -14
  310. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_router_star_broadcast_routing.py +10 -2
  311. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_router_unsigned_excludes_https_s2s.py +17 -3
  312. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_signaling_broker.py +4 -1
  313. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_signaling_mailbox.py +34 -13
  314. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_signaling_select.py +1 -0
  315. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_skfed_announce.py +17 -7
  316. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_skfed_cli.py +12 -1
  317. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_skfed_directory.py +4 -5
  318. skcomms-0.2.4/tests/test_skfed_dns.sh +75 -0
  319. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_skfed_operator_pin.py +18 -5
  320. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_skfed_readdr.py +7 -6
  321. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_skfed_resolve.py +14 -12
  322. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_skworld_manifest.py +3 -11
  323. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_slack_adapter.py +16 -18
  324. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_ssrf_guard.py +4 -14
  325. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_store_forward.py +10 -14
  326. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_syncthing_rail.py +6 -4
  327. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_telegram_adapter.py +28 -19
  328. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_typed_message_content.py +3 -10
  329. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_webrtc_ice_logging.py +1 -1
  330. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_webrtc_signaling_log_dedup.py +3 -3
  331. {skcomms-0.2.2 → skcomms-0.2.4}/LICENSE +0 -0
  332. {skcomms-0.2.2 → skcomms-0.2.4}/README.md +0 -0
  333. {skcomms-0.2.2 → skcomms-0.2.4}/setup.cfg +0 -0
  334. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/access/audit.py +0 -0
  335. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/access/registry.py +0 -0
  336. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/adapters/__init__.py +0 -0
  337. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/adapters/base.py +0 -0
  338. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/adapters/factory.py +0 -0
  339. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/adapters/fake.py +0 -0
  340. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/adapters/models.py +0 -0
  341. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/compression.py +0 -0
  342. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/discovery.py +0 -0
  343. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/glossa/__init__.py +0 -0
  344. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/heartbeat.py +0 -0
  345. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/home.py +0 -0
  346. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/household_router.py +0 -0
  347. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/identity.py +0 -0
  348. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/marketplace.py +0 -0
  349. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/metrics.py +0 -0
  350. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/models.py +0 -0
  351. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/node_registry.py +0 -0
  352. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/pairing_mirror.py +0 -0
  353. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/paths.py +0 -0
  354. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/pq_provider.py +0 -0
  355. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/pqkem_backend.py +0 -0
  356. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/profile_router.py +0 -0
  357. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/pubsub.py +0 -0
  358. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/pubsub_transport.py +0 -0
  359. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/queue.py +0 -0
  360. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/ratelimit.py +0 -0
  361. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/realm.py +0 -0
  362. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/signaling.py +0 -0
  363. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/skworld_manifest.py +0 -0
  364. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/souls_router.py +0 -0
  365. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transport.py +0 -0
  366. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/__init__.py +0 -0
  367. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/audio_track.py +0 -0
  368. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/ble/__init__.py +0 -0
  369. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/ble/radio.py +0 -0
  370. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/broker_server.py +0 -0
  371. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/lora/__init__.py +0 -0
  372. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/nostr.py +0 -0
  373. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/p2p_connector.py +0 -0
  374. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/signaling_base.py +0 -0
  375. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/syncthing.py +0 -0
  376. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/tailscale.py +0 -0
  377. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/video_track.py +0 -0
  378. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/webrtc_media.py +0 -0
  379. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms/transports/websocket.py +0 -0
  380. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms.egg-info/dependency_links.txt +0 -0
  381. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms.egg-info/entry_points.txt +0 -0
  382. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms.egg-info/requires.txt +0 -0
  383. {skcomms-0.2.2 → skcomms-0.2.4}/src/skcomms.egg-info/top_level.txt +0 -0
  384. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_adapter_fake.py +0 -0
  385. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_api_access_token.py +0 -0
  386. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_api_health.py +0 -0
  387. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_ble_gatt.py +0 -0
  388. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_ble_mesh.py +0 -0
  389. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_ble_pairing_bundle.py +0 -0
  390. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_ble_radio.py +0 -0
  391. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_capauth_revoked_expired.py +0 -0
  392. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_cluster_schema.py +0 -0
  393. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_consent_ban_gate_fail_closed.py +0 -0
  394. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_crypto_failclosed.py +0 -0
  395. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_daemon_adapter_lifecycle.py +0 -0
  396. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_deadletter_ops.py +0 -0
  397. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_encrypt_fail_closed.py +0 -0
  398. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_envelope_consent_token.py +0 -0
  399. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_http_s2s_star_recipient.py +0 -0
  400. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_hybrid_envelope_sig.py +0 -0
  401. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_lora_meshtastic_iface.py +0 -0
  402. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_paths_scoping.py +0 -0
  403. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_peer_inbox_traversal.py +0 -0
  404. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_pqc_shim.py +0 -0
  405. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_pqroute.py +0 -0
  406. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_prune_inbox.py +0 -0
  407. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_router_perm_backoff.py +0 -0
  408. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_sign_fail_closed.py +0 -0
  409. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_skfed_ssrf.py +0 -0
  410. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_smoke.py +0 -0
  411. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_syncthing_recipient_guard.py +0 -0
  412. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_tofu.py +0 -0
  413. {skcomms-0.2.2 → skcomms-0.2.4}/tests/test_webrtc_bridge_lifecycle.py +0 -0
@@ -0,0 +1,99 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.10", "3.11", "3.12"]
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ with:
18
+ # setuptools-scm derives the version from the git tag, so a
19
+ # shallow checkout would yield a placeholder like 0.1.dev1 and
20
+ # break dependency resolution against pinned siblings.
21
+ fetch-depth: 0
22
+ fetch-tags: true
23
+ - uses: actions/setup-python@v5
24
+ with:
25
+ python-version: ${{ matrix.python-version }}
26
+ - name: Install dependencies
27
+ # -c constraints.txt pins every dependency to the versions this repo
28
+ # validated, so CI does not silently drift onto newer releases.
29
+ run: pip install -c constraints.txt -e ".[crypto,dev,pqc]"
30
+ # skcomms does NOT depend on skcapstone (that would invert the direction),
31
+ # but tests/test_integration.py exercises "integrated" mode and assumed a
32
+ # dev venv where skcapstone happens to be installed. In CI it is not, so
33
+ # those tests errored with ModuleNotFoundError instead of testing anything.
34
+ # skcoord is named too (skcapstone.coordination is a re-export shim over it),
35
+ # and skcot, which backs geo_store.SkcotGeoStore and is git-only (not on PyPI,
36
+ # not declared), so CI could never have exercised the skcot bridge. --no-deps
37
+ # also stops this dragging a published skcomms over the checkout under test.
38
+ - name: Install skcapstone (optional integration backbone) from source
39
+ run: |
40
+ pip install --no-deps --no-cache-dir \
41
+ "skcapstone @ git+https://github.com/smilinTux/skcapstone" \
42
+ "skcoord @ git+https://github.com/smilinTux/skcoord" \
43
+ "skcot @ git+https://github.com/smilinTux/skcot" \
44
+ "capauth @ git+https://github.com/smilinTux/capauth"
45
+ - name: Run tests with coverage
46
+ run: python -m pytest tests/ -v --tb=short --cov=skcomms --cov-report=xml --cov-report=term-missing
47
+ - name: Upload coverage
48
+ if: matrix.python-version == '3.12'
49
+ uses: codecov/codecov-action@v4
50
+ with:
51
+ files: coverage.xml
52
+ fail_ci_if_error: false
53
+
54
+ lint:
55
+ runs-on: ubuntu-latest
56
+ steps:
57
+ - uses: actions/checkout@v4
58
+ with:
59
+ # setuptools-scm derives the version from the git tag, so a
60
+ # shallow checkout would yield a placeholder like 0.1.dev1 and
61
+ # break dependency resolution against pinned siblings.
62
+ fetch-depth: 0
63
+ fetch-tags: true
64
+ - uses: actions/setup-python@v5
65
+ with:
66
+ python-version: "3.12"
67
+ # Versions come from constraints.txt (black==26.5.1, ruff==0.15.21),
68
+ # which already does the pinning correctly. Do NOT re-pin them inline:
69
+ # naming a different version here makes pip unsolvable against the
70
+ # constraint file, which fails the job on install before it lints
71
+ # anything at all.
72
+ - name: Install lint tools
73
+ run: pip install -c constraints.txt black ruff
74
+ - name: Check formatting
75
+ run: black --check src/ tests/
76
+ - name: Lint
77
+ run: ruff check src/
78
+
79
+ build:
80
+ runs-on: ubuntu-latest
81
+ steps:
82
+ - uses: actions/checkout@v4
83
+ with:
84
+ # setuptools-scm derives the version from the git tag, so a
85
+ # shallow checkout would yield a placeholder like 0.1.dev1 and
86
+ # break dependency resolution against pinned siblings.
87
+ fetch-depth: 0
88
+ fetch-tags: true
89
+ - uses: actions/setup-python@v5
90
+ with:
91
+ python-version: "3.12"
92
+ - name: Build wheel and sdist
93
+ run: |
94
+ pip install build
95
+ python -m build
96
+ - name: Check dist
97
+ run: |
98
+ pip install twine
99
+ twine check dist/*
@@ -0,0 +1,134 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ branches: ["main"]
6
+ tags: ["v*"]
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ # Trusted Publishing (OIDC) is configured on PyPI for this project:
13
+ # owner=smilinTux workflow=publish.yml environment=pypi
14
+ # No PyPI token is used in the publish path. The full test suite is intentionally
15
+ # NOT a gate here (that lives in ci.yml on PRs); a tagged release only needs to
16
+ # build cleanly and pass `twine check`, so a red/flaky unrelated test never blocks
17
+ # a release again.
18
+ concurrency:
19
+ # Never let two releases race: a second push to main waits rather than
20
+ # computing the same "next" tag from a stale view of the tag list.
21
+ group: publish-${{ github.ref }}
22
+ cancel-in-progress: false
23
+
24
+ jobs:
25
+ # ---------------------------------------------------------------------- #
26
+ # Cut the release tag. Only on a push to main; skipped on a tag push.
27
+ # ---------------------------------------------------------------------- #
28
+ tag:
29
+ if: github.ref == 'refs/heads/main'
30
+ runs-on: ubuntu-latest
31
+ permissions:
32
+ contents: write
33
+ outputs:
34
+ tagged: ${{ steps.bump.outputs.tagged }}
35
+ steps:
36
+ - uses: actions/checkout@v4
37
+ with:
38
+ # Full history + tags, or `git describe` cannot see the latest tag and
39
+ # every run would compute v0.0.1.
40
+ fetch-depth: 0
41
+ fetch-tags: true
42
+ - id: bump
43
+ name: Cut the next patch tag if HEAD is not already tagged
44
+ run: |
45
+ set -euo pipefail
46
+ git config user.name "github-actions[bot]"
47
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
48
+
49
+ latest="$(git describe --tags --abbrev=0 --match 'v[0-9]*' 2>/dev/null || echo '')"
50
+ head_sha="$(git rev-parse HEAD)"
51
+
52
+ if [ -n "$latest" ] && [ "$(git rev-list -n1 "$latest")" = "$head_sha" ]; then
53
+ echo "HEAD is already $latest; nothing to release."
54
+ echo "tagged=false" >> "$GITHUB_OUTPUT"
55
+ exit 0
56
+ fi
57
+
58
+ base="${latest:-v0.0.0}"
59
+ ver="${base#v}"
60
+ IFS='.' read -r major minor patch <<< "$ver"
61
+ major="${major:-0}"; minor="${minor:-0}"; patch="${patch:-0}"
62
+ next="v${major}.${minor}.$((patch + 1))"
63
+ while git rev-parse -q --verify "refs/tags/$next" >/dev/null 2>&1; do
64
+ patch=$((patch + 1))
65
+ next="v${major}.${minor}.$((patch + 1))"
66
+ done
67
+
68
+ echo "Cutting $next at ${head_sha:0:8} (previous: ${latest:-none})"
69
+ git tag -a "$next" -m "release: $next from main"
70
+ git push origin "$next"
71
+ echo "tagged=true" >> "$GITHUB_OUTPUT"
72
+
73
+ build:
74
+ needs: [tag]
75
+ # `always()` is required because `needs` on a SKIPPED job would otherwise
76
+ # skip this one on the tag-push path.
77
+ if: >-
78
+ always() && !cancelled() &&
79
+ (needs.tag.result == 'skipped' || needs.tag.outputs.tagged == 'true')
80
+ runs-on: ubuntu-latest
81
+ steps:
82
+ - uses: actions/checkout@v4
83
+ with:
84
+ # setuptools-scm derives the version from the tag, so the tag the job
85
+ # above just pushed must be visible here or the build would produce a
86
+ # dev version with a local segment (+g<sha>), which PyPI rejects.
87
+ fetch-depth: 0
88
+ fetch-tags: true
89
+ - name: Refuse to publish a tag that is not on main
90
+ # The local pre-push hook is a symlink into each clone's working tree, so
91
+ # a clone that has not pulled the main-only fix keeps cutting tags at
92
+ # feature-branch tips and pushing them. This is the server-side backstop:
93
+ # it holds no matter what any developer's clone does.
94
+ #
95
+ # Override with the ALLOW_OFF_MAIN_RELEASE repository variable if a
96
+ # release ever legitimately needs to come off a maintenance branch.
97
+ if: startsWith(github.ref, 'refs/tags/')
98
+ run: |
99
+ set -euo pipefail
100
+ if [ "${{ vars.ALLOW_OFF_MAIN_RELEASE }}" = "1" ]; then
101
+ echo "ALLOW_OFF_MAIN_RELEASE=1 is set; skipping the on-main check."
102
+ exit 0
103
+ fi
104
+ git fetch --no-tags origin main:refs/remotes/origin/main
105
+ if git merge-base --is-ancestor "$GITHUB_SHA" refs/remotes/origin/main; then
106
+ echo "OK: ${GITHUB_REF_NAME} (${GITHUB_SHA:0:8}) is on main."
107
+ exit 0
108
+ fi
109
+ echo "::error::${GITHUB_REF_NAME} points at ${GITHUB_SHA:0:8}, which is NOT on main. Refusing to publish."
110
+ echo "::error::A tag cut off a feature branch must not become a release. Delete the tag, merge to main, and let the main push cut the release."
111
+ exit 1
112
+ - uses: actions/setup-python@v5
113
+ with:
114
+ python-version: "3.12"
115
+ - run: python -m pip install --upgrade build twine
116
+ - run: python -m build
117
+ - run: python -m twine check dist/*
118
+ - uses: actions/upload-artifact@v4
119
+ with:
120
+ name: dist
121
+ path: dist/
122
+
123
+ pypi-publish:
124
+ needs: build
125
+ runs-on: ubuntu-latest
126
+ environment: pypi
127
+ permissions:
128
+ id-token: write
129
+ steps:
130
+ - uses: actions/download-artifact@v4
131
+ with:
132
+ name: dist
133
+ path: dist/
134
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,127 @@
1
+ name: pytest
2
+
3
+ # Runs the skcomms pytest suite on GitHub-hosted runners.
4
+ #
5
+ # GREEN-by-construction: this mirrors the repo's own known-passing invocation
6
+ # (see qa.yml) — install the package with its `dev` extras, then run the WHOLE
7
+ # suite. The suite is CI-clean: it requires no live external services and no
8
+ # sibling sk* packages.
9
+ #
10
+ # Honest notes on scope (verified 2026-06-28):
11
+ # * There are NO custom pytest markers in this repo (no `integration`,
12
+ # `e2e_live`, `bunker`, etc. registered or applied). So there is nothing to
13
+ # exclude with `-m "not ..."` — adding such a filter would be fabricated.
14
+ # * Optional-transport tests (telegram/discord/webrtc/zeroconf/liboqs) and the
15
+ # capauth/skcapstone-dependent paths self-skip via pytest.importorskip /
16
+ # @pytest.mark.skipif when those packages are absent. The `dev` extras
17
+ # (pytest, pytest-asyncio, fastapi, httpx, pgpy, websockets, cryptography)
18
+ # are the deliberate known-green dependency set; sibling sk* packages
19
+ # (capauth, skcapstone, sk-pqc) are NOT installed and the suite stays green
20
+ # without them.
21
+
22
+ on:
23
+ push:
24
+ branches: ["main"]
25
+ pull_request:
26
+ branches: ["main"]
27
+
28
+ permissions:
29
+ contents: read
30
+
31
+ jobs:
32
+ pytest:
33
+ runs-on: ubuntu-latest
34
+ strategy:
35
+ fail-fast: false
36
+ matrix:
37
+ python-version: ["3.11", "3.12"]
38
+ steps:
39
+ - uses: actions/checkout@v4
40
+ with:
41
+ # setuptools-scm derives the version from the git tag, so a
42
+ # shallow checkout would yield a placeholder like 0.1.dev1 and
43
+ # break dependency resolution against pinned siblings.
44
+ fetch-depth: 0
45
+ fetch-tags: true
46
+ - uses: actions/setup-python@v5
47
+ with:
48
+ python-version: ${{ matrix.python-version }}
49
+ cache: pip
50
+ - name: Install skcomms + dev extras
51
+ # -c constraints.txt pins the dev/runtime set to the versions this repo
52
+ # validated, so a fresh runner installs exactly what CI last tested.
53
+ run: |
54
+ python -m pip install --upgrade pip
55
+ pip install -c constraints.txt -e ".[crypto,dev,pqc]"
56
+ # skcomms does NOT depend on skcapstone (that would invert the direction),
57
+ # but tests/test_integration.py exercises "integrated" mode and assumed a
58
+ # dev venv where skcapstone happens to be installed. In CI it is not, so
59
+ # those tests errored with ModuleNotFoundError instead of testing anything.
60
+ # skcoord is named too (skcapstone.coordination is a re-export shim over it),
61
+ # and skcot, which backs geo_store.SkcotGeoStore and is git-only (not on PyPI,
62
+ # not declared), so CI could never have exercised the skcot bridge. --no-deps
63
+ # also stops this dragging a published skcomms over the checkout under test.
64
+ - name: Install skcapstone (optional integration backbone) from source
65
+ run: |
66
+ pip install --no-deps --no-cache-dir \
67
+ "skcapstone @ git+https://github.com/smilinTux/skcapstone" \
68
+ "skcoord @ git+https://github.com/smilinTux/skcoord" \
69
+ "skcot @ git+https://github.com/smilinTux/skcot" \
70
+ "capauth @ git+https://github.com/smilinTux/capauth"
71
+ - name: Run pytest (full suite)
72
+ run: python -m pytest tests/ -q
73
+
74
+ # The pqdm/pqdm2 crypto gates are the ONE place a wire-format drift between
75
+ # this repo and the Dart client (sk-pqc-dart / skworld-app) gets caught, and
76
+ # they are exactly the tests the `dev`-extras job above cannot run: every
77
+ # hybrid KEM op needs liboqs, and pqdm.open_multi() swallows a missing backend
78
+ # into a plain `None` return, so without liboqs the gate cannot fail-loud — it
79
+ # self-skips. This job installs the `pqc` extra so the gate genuinely runs.
80
+ #
81
+ # liboqs-python builds liboqs from source into ~/_oqs on first import when it
82
+ # cannot find a system copy, so that directory is cached across runs.
83
+ pqc-interop:
84
+ runs-on: ubuntu-latest
85
+ steps:
86
+ - uses: actions/checkout@v4
87
+ with:
88
+ # setuptools-scm derives the version from the git tag, so a
89
+ # shallow checkout would yield a placeholder like 0.1.dev1 and
90
+ # break dependency resolution against pinned siblings.
91
+ fetch-depth: 0
92
+ fetch-tags: true
93
+ - uses: actions/setup-python@v5
94
+ with:
95
+ python-version: "3.12"
96
+ cache: pip
97
+ - name: Cache the liboqs build
98
+ uses: actions/cache@v4
99
+ with:
100
+ path: ~/_oqs
101
+ key: liboqs-${{ runner.os }}-${{ hashFiles('constraints.txt') }}
102
+ - name: Install skcomms + dev + pqc extras
103
+ run: |
104
+ python -m pip install --upgrade pip
105
+ pip install -c constraints.txt -e ".[crypto,dev,pqc]"
106
+ # skcomms does NOT depend on skcapstone (that would invert the direction),
107
+ # but tests/test_integration.py exercises "integrated" mode and assumed a
108
+ # dev venv where skcapstone happens to be installed. In CI it is not, so
109
+ # those tests errored with ModuleNotFoundError instead of testing anything.
110
+ # skcoord is named too (skcapstone.coordination is a re-export shim over it),
111
+ # and skcot, which backs geo_store.SkcotGeoStore and is git-only (not on PyPI,
112
+ # not declared), so CI could never have exercised the skcot bridge. --no-deps
113
+ # also stops this dragging a published skcomms over the checkout under test.
114
+ - name: Install skcapstone (optional integration backbone) from source
115
+ run: |
116
+ pip install --no-deps --no-cache-dir \
117
+ "skcapstone @ git+https://github.com/smilinTux/skcapstone" \
118
+ "skcoord @ git+https://github.com/smilinTux/skcoord" \
119
+ "skcot @ git+https://github.com/smilinTux/skcot" \
120
+ "capauth @ git+https://github.com/smilinTux/capauth"
121
+ - name: Assert the PQ backend is really present
122
+ # Without this the crypto gates would silently self-skip and the job
123
+ # would report green while proving nothing.
124
+ run: |
125
+ python -c "from skcomms import pqkem; assert pqkem.is_available(), 'liboqs missing: the pqdm gates would self-skip'"
126
+ - name: Run the pqdm / pqdm2 interop gates
127
+ run: python -m pytest tests/test_pqdm.py tests/test_pqdm2.py tests/test_pqdm2_interop.py -q
@@ -0,0 +1,55 @@
1
+ name: QA
2
+
3
+ # Runs the full skcomms QA pytest suite on every push to main, every PR, and
4
+ # every version tag. The suite is CI-clean (no live external services required),
5
+ # so we run it whole.
6
+
7
+ on:
8
+ push:
9
+ branches: ["main"]
10
+ tags: ["v*"]
11
+ pull_request:
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ jobs:
17
+ qa:
18
+ runs-on: ubuntu-latest
19
+ strategy:
20
+ fail-fast: false
21
+ matrix:
22
+ python-version: ["3.10", "3.11", "3.12"]
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+ with:
26
+ # setuptools-scm derives the version from the git tag, so a
27
+ # shallow checkout would yield a placeholder like 0.1.dev1 and
28
+ # break dependency resolution against pinned siblings.
29
+ fetch-depth: 0
30
+ fetch-tags: true
31
+ - uses: actions/setup-python@v5
32
+ with:
33
+ python-version: ${{ matrix.python-version }}
34
+ - name: Install package + dev deps
35
+ # -c constraints.txt pins to the versions this repo validated (inert on
36
+ # the fallback branch, which installs a smaller set).
37
+ run: |
38
+ pip install -c constraints.txt -e ".[crypto,dev,pqc]" || { pip install -c constraints.txt -e . && pip install -c constraints.txt pytest pytest-asyncio; }
39
+ # skcomms does NOT depend on skcapstone (that would invert the direction),
40
+ # but tests/test_integration.py exercises "integrated" mode and assumed a
41
+ # dev venv where skcapstone happens to be installed. In CI it is not, so
42
+ # those tests errored with ModuleNotFoundError instead of testing anything.
43
+ # skcoord is named too (skcapstone.coordination is a re-export shim over it),
44
+ # and skcot, which backs geo_store.SkcotGeoStore and is git-only (not on PyPI,
45
+ # not declared), so CI could never have exercised the skcot bridge. --no-deps
46
+ # also stops this dragging a published skcomms over the checkout under test.
47
+ - name: Install skcapstone (optional integration backbone) from source
48
+ run: |
49
+ pip install --no-deps --no-cache-dir \
50
+ "skcapstone @ git+https://github.com/smilinTux/skcapstone" \
51
+ "skcoord @ git+https://github.com/smilinTux/skcoord" \
52
+ "skcot @ git+https://github.com/smilinTux/skcot" \
53
+ "capauth @ git+https://github.com/smilinTux/capauth"
54
+ - name: Run QA pytest suite
55
+ run: python -m pytest tests/ -q
@@ -0,0 +1,48 @@
1
+ name: secret-scan
2
+
3
+ # Secret-scan gate: keeps the repo's clean secrets hygiene true over time.
4
+ #
5
+ # Runs gitleaks (the fleet default, via its official GitHub Action) on every
6
+ # push and every pull request. The build FAILS if gitleaks finds anything not
7
+ # covered by the baseline allowlist in .gitleaks.toml at the repo root.
8
+ #
9
+ # The allowlist documents known non-secret placeholder tokens; see that file
10
+ # for how to add an entry when a real placeholder trips the scanner. If the
11
+ # scanner flags a REAL secret, rotate it and purge it from history, do NOT
12
+ # add it to the allowlist.
13
+
14
+ on:
15
+ push:
16
+ branches: ["main"]
17
+ pull_request:
18
+ branches: ["main"]
19
+
20
+ permissions:
21
+ contents: read
22
+
23
+ jobs:
24
+ gitleaks:
25
+ runs-on: ubuntu-latest
26
+ steps:
27
+ - uses: actions/checkout@v4
28
+ with:
29
+ # Full history so a push scans every new commit, not just the tip.
30
+ fetch-depth: 0
31
+ - name: Run gitleaks
32
+ uses: gitleaks/gitleaks-action@v2
33
+ env:
34
+ # Use the baseline/allowlist committed at the repo root.
35
+ GITLEAKS_CONFIG: ${{ github.workspace }}/.gitleaks.toml
36
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
37
+ # gitleaks-action requires a license for ORGANIZATION-owned repos.
38
+ # The note here used to say this repo ran on an individual account;
39
+ # it does not, it is owned by the smilinTux org, which is why every
40
+ # run fails with "missing gitleaks license" before scanning anything.
41
+ # Licensing keys on org-vs-personal, not public-vs-private, so being
42
+ # a public repo does not exempt it.
43
+ #
44
+ # Free key for open source at https://gitleaks.io; add it as an
45
+ # organization secret named GITLEAKS_LICENSE so every smilinTux repo
46
+ # can use the same one. Until it exists this reference is harmless:
47
+ # an unset secret expands to empty, exactly as today.
48
+ GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}
@@ -0,0 +1,16 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .venv/
8
+ .env
9
+ .pytest_cache/
10
+ .mypy_cache/
11
+ .ruff_cache/
12
+ *.egg
13
+ .eggs/
14
+ .coverage
15
+ htmlcov/
16
+ MANIFEST.in
@@ -0,0 +1,35 @@
1
+ # gitleaks configuration for skcomms
2
+ #
3
+ # This is the baseline/allowlist that pairs with the secret-scan CI gate
4
+ # (.github/workflows/secret-scan.yml). It keeps the full set of gitleaks
5
+ # default rules and layers on an allowlist for known, non-secret placeholder
6
+ # tokens so the build only fails on genuine findings.
7
+ #
8
+ # How to use it:
9
+ # * The CI gate (gitleaks GitHub Action) reads this file automatically because
10
+ # it lives at the repo root and is named .gitleaks.toml.
11
+ # * To scan locally the same way CI does:
12
+ # docker run --rm -v "$(pwd):/repo" zricethezav/gitleaks:latest \
13
+ # detect --source /repo -c /repo/.gitleaks.toml -v
14
+ # * If gitleaks flags a value that is a documented placeholder or fixture
15
+ # (not a real secret), add it below with a justification comment. If it
16
+ # flags a REAL secret, rotate it and purge it from history; do NOT allowlist.
17
+
18
+ title = "skcomms gitleaks config"
19
+
20
+ # Start from the full gitleaks default ruleset, then extend with the allowlist
21
+ # below. Without this, only our custom rules would run.
22
+ [extend]
23
+ useDefault = true
24
+
25
+ # Global allowlists apply to every rule.
26
+ [[allowlists]]
27
+ description = """
28
+ Prose token "Ed25519-signed" in the BLE mesh proximity transport design spec.
29
+ The generic-api-key rule flags the hyphenated cryptography term (high entropy)
30
+ even though it is plain documentation text describing that packets are signed
31
+ with Ed25519, not a credential. Confirmed non-secret on 2026-07-10 by reading
32
+ docs/superpowers/specs/2026-06-13-ble-mesh-proximity-transport-design.md:178.
33
+ """
34
+ paths = ['''docs/superpowers/specs/2026-06-13-ble-mesh-proximity-transport-design\.md''']
35
+ regexes = ['''Ed25519-signed''']