skcomms 0.2.7__tar.gz → 0.2.9__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 (412) hide show
  1. skcomms-0.2.9/.github/workflows/secret-scan.yml +61 -0
  2. {skcomms-0.2.7 → skcomms-0.2.9}/.gitleaks.toml +12 -0
  3. {skcomms-0.2.7/src/skcomms.egg-info → skcomms-0.2.9}/PKG-INFO +1 -1
  4. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/pairing_mirror.py +46 -1
  5. {skcomms-0.2.7 → skcomms-0.2.9/src/skcomms.egg-info}/PKG-INFO +1 -1
  6. {skcomms-0.2.7 → skcomms-0.2.9}/tests/conftest.py +11 -0
  7. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_pairing_mirror.py +51 -0
  8. skcomms-0.2.7/.github/workflows/secret-scan.yml +0 -48
  9. {skcomms-0.2.7 → skcomms-0.2.9}/.github/workflows/ci.yml +0 -0
  10. {skcomms-0.2.7 → skcomms-0.2.9}/.github/workflows/publish.yml +0 -0
  11. {skcomms-0.2.7 → skcomms-0.2.9}/.github/workflows/pytest.yml +0 -0
  12. {skcomms-0.2.7 → skcomms-0.2.9}/.github/workflows/qa.yml +0 -0
  13. {skcomms-0.2.7 → skcomms-0.2.9}/.gitignore +0 -0
  14. {skcomms-0.2.7 → skcomms-0.2.9}/CHANGELOG.md +0 -0
  15. {skcomms-0.2.7 → skcomms-0.2.9}/CODE_OF_CONDUCT.md +0 -0
  16. {skcomms-0.2.7 → skcomms-0.2.9}/CONTRIBUTING.md +0 -0
  17. {skcomms-0.2.7 → skcomms-0.2.9}/DEPLOY-NOTES.md +0 -0
  18. {skcomms-0.2.7 → skcomms-0.2.9}/Dockerfile +0 -0
  19. {skcomms-0.2.7 → skcomms-0.2.9}/LICENSE +0 -0
  20. {skcomms-0.2.7 → skcomms-0.2.9}/MISSION.md +0 -0
  21. {skcomms-0.2.7 → skcomms-0.2.9}/README.md +0 -0
  22. {skcomms-0.2.7 → skcomms-0.2.9}/SECURITY.md +0 -0
  23. {skcomms-0.2.7 → skcomms-0.2.9}/SOP.md +0 -0
  24. {skcomms-0.2.7 → skcomms-0.2.9}/constraints.txt +0 -0
  25. {skcomms-0.2.7 → skcomms-0.2.9}/contrib/systemd/skcomms-api.service +0 -0
  26. {skcomms-0.2.7 → skcomms-0.2.9}/contrib/systemd/skcomms-housekeep.service +0 -0
  27. {skcomms-0.2.7 → skcomms-0.2.9}/contrib/systemd/skcomms-housekeep.timer +0 -0
  28. {skcomms-0.2.7 → skcomms-0.2.9}/docs/ARCHITECTURE.md +0 -0
  29. {skcomms-0.2.7 → skcomms-0.2.9}/docs/DISCORD_SETUP.md +0 -0
  30. {skcomms-0.2.7 → skcomms-0.2.9}/docs/HANDOFF-skfed-s2s-opus-delivery.md +0 -0
  31. {skcomms-0.2.7 → skcomms-0.2.9}/docs/PAIRING.md +0 -0
  32. {skcomms-0.2.7 → skcomms-0.2.9}/docs/REVOCATION_LATENCY.md +0 -0
  33. {skcomms-0.2.7 → skcomms-0.2.9}/docs/SYNCTHING_TOPOLOGY.md +0 -0
  34. {skcomms-0.2.7 → skcomms-0.2.9}/docs/access-plane-p7.md +0 -0
  35. {skcomms-0.2.7 → skcomms-0.2.9}/docs/access-plane-p8.md +0 -0
  36. {skcomms-0.2.7 → skcomms-0.2.9}/docs/cot-bridge.md +0 -0
  37. {skcomms-0.2.7 → skcomms-0.2.9}/docs/crypto-architecture.md +0 -0
  38. {skcomms-0.2.7 → skcomms-0.2.9}/docs/deploy-plan/skcomms-bulletproof-deploy.md +0 -0
  39. {skcomms-0.2.7 → skcomms-0.2.9}/docs/federation-data-comms-architecture.md +0 -0
  40. {skcomms-0.2.7 → skcomms-0.2.9}/docs/funnel-privacy.md +0 -0
  41. {skcomms-0.2.7 → skcomms-0.2.9}/docs/mode-b-tailnet-deploy.md +0 -0
  42. {skcomms-0.2.7 → skcomms-0.2.9}/docs/node_registry.example.yml +0 -0
  43. {skcomms-0.2.7 → skcomms-0.2.9}/docs/rfcs/RFC-0001-pq-ratchet-metadata-dual-identity.md +0 -0
  44. {skcomms-0.2.7 → skcomms-0.2.9}/docs/skfed-consent-design.md +0 -0
  45. {skcomms-0.2.7 → skcomms-0.2.9}/docs/skfed-federation-guide.md +0 -0
  46. {skcomms-0.2.7 → skcomms-0.2.9}/docs/state-sync-rearch-plan.md +0 -0
  47. {skcomms-0.2.7 → skcomms-0.2.9}/docs/superpowers/plans/2026-06-10-qr-pairing.md +0 -0
  48. {skcomms-0.2.7 → skcomms-0.2.9}/docs/superpowers/plans/2026-06-13-ble-mesh-p1-core.md +0 -0
  49. {skcomms-0.2.7 → skcomms-0.2.9}/docs/superpowers/plans/2026-06-13-lora-l1-core.md +0 -0
  50. {skcomms-0.2.7 → skcomms-0.2.9}/docs/superpowers/plans/2026-06-13-skglossa-g1-core.md +0 -0
  51. {skcomms-0.2.7 → skcomms-0.2.9}/docs/superpowers/plans/2026-06-13-skglossa-g2-macro-lexicon.md +0 -0
  52. {skcomms-0.2.7 → skcomms-0.2.9}/docs/superpowers/plans/2026-06-13-skglossa-g4-emergent.md +0 -0
  53. {skcomms-0.2.7 → skcomms-0.2.9}/docs/superpowers/plans/2026-06-13-tier3-adapter-runtime.md +0 -0
  54. {skcomms-0.2.7 → skcomms-0.2.9}/docs/superpowers/specs/2026-06-13-ble-mesh-proximity-transport-design.md +0 -0
  55. {skcomms-0.2.7 → skcomms-0.2.9}/docs/superpowers/specs/2026-06-13-lora-meshtastic-transport-design.md +0 -0
  56. {skcomms-0.2.7 → skcomms-0.2.9}/docs/superpowers/specs/2026-06-13-skglossa-design.md +0 -0
  57. {skcomms-0.2.7 → skcomms-0.2.9}/docs/tak-vs-skfed-analysis.md +0 -0
  58. {skcomms-0.2.7 → skcomms-0.2.9}/pyproject.toml +0 -0
  59. {skcomms-0.2.7 → skcomms-0.2.9}/scripts/backfill_file_locations.py +0 -0
  60. {skcomms-0.2.7 → skcomms-0.2.9}/scripts/bootstrap.sh +0 -0
  61. {skcomms-0.2.7 → skcomms-0.2.9}/scripts/discord_smoke.py +0 -0
  62. {skcomms-0.2.7 → skcomms-0.2.9}/scripts/drain_orphan_outbox.py +0 -0
  63. {skcomms-0.2.7 → skcomms-0.2.9}/scripts/mode-b-setup.sh +0 -0
  64. {skcomms-0.2.7 → skcomms-0.2.9}/scripts/refresh-constraints.sh +0 -0
  65. {skcomms-0.2.7 → skcomms-0.2.9}/scripts/skfed-dns.sh +0 -0
  66. {skcomms-0.2.7 → skcomms-0.2.9}/scripts/skfed-neutral-addresses.sh +0 -0
  67. {skcomms-0.2.7 → skcomms-0.2.9}/scripts/skfed-sync-rearch.sh +0 -0
  68. {skcomms-0.2.7 → skcomms-0.2.9}/scripts/telegram_smoke.py +0 -0
  69. {skcomms-0.2.7 → skcomms-0.2.9}/scripts/templates/consent-mode.conf.tmpl +0 -0
  70. {skcomms-0.2.7 → skcomms-0.2.9}/scripts/templates/realms.yml.tmpl +0 -0
  71. {skcomms-0.2.7 → skcomms-0.2.9}/scripts/templates/tailscale-serve.sh.tmpl +0 -0
  72. {skcomms-0.2.7 → skcomms-0.2.9}/scripts/test_skfed_sync_rearch.sh +0 -0
  73. {skcomms-0.2.7 → skcomms-0.2.9}/setup.cfg +0 -0
  74. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/__init__.py +0 -0
  75. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/access/__init__.py +0 -0
  76. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/access/audit.py +0 -0
  77. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/access/config.py +0 -0
  78. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/access/exec.py +0 -0
  79. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/access/files.py +0 -0
  80. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/access/grants.py +0 -0
  81. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/access/knowledge.py +0 -0
  82. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/access/registry.py +0 -0
  83. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/access/routing.py +0 -0
  84. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/access/server.py +0 -0
  85. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/access/wiring.py +0 -0
  86. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/ack.py +0 -0
  87. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/adapters/__init__.py +0 -0
  88. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/adapters/base.py +0 -0
  89. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/adapters/discord.py +0 -0
  90. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/adapters/factory.py +0 -0
  91. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/adapters/fake.py +0 -0
  92. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/adapters/matrix.py +0 -0
  93. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/adapters/models.py +0 -0
  94. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/adapters/registry.py +0 -0
  95. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/adapters/slack.py +0 -0
  96. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/adapters/telegram.py +0 -0
  97. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/anon_queue.py +0 -0
  98. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/anon_transport.py +0 -0
  99. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/api.py +0 -0
  100. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/capabilities.py +0 -0
  101. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/capauth_validator.py +0 -0
  102. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/cli.py +0 -0
  103. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/cluster.py +0 -0
  104. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/compression.py +0 -0
  105. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/config.py +0 -0
  106. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/consent.py +0 -0
  107. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/consent_banfeeds.py +0 -0
  108. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/consent_captcha.py +0 -0
  109. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/consent_greylist.py +0 -0
  110. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/consent_group_gate.py +0 -0
  111. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/consent_groups.py +0 -0
  112. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/consent_moderation.py +0 -0
  113. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/consent_pipeline.py +0 -0
  114. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/consent_policy.py +0 -0
  115. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/consent_requests.py +0 -0
  116. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/consent_runtime.py +0 -0
  117. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/consent_tiering.py +0 -0
  118. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/consent_tokens.py +0 -0
  119. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/core.py +0 -0
  120. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/crypto.py +0 -0
  121. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/crypto_suites.py +0 -0
  122. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/did_router.py +0 -0
  123. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/discovery.py +0 -0
  124. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/envelope.py +0 -0
  125. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/federation.py +0 -0
  126. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/geo_seed.py +0 -0
  127. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/geo_store.py +0 -0
  128. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/glossa/__init__.py +0 -0
  129. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/glossa/codebook.py +0 -0
  130. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/glossa/codec.py +0 -0
  131. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/glossa/emergent.py +0 -0
  132. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/glossa/gloss.py +0 -0
  133. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/glossa/handshake.py +0 -0
  134. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/glossa/macros.py +0 -0
  135. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/glossa/message.py +0 -0
  136. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/glossa/session.py +0 -0
  137. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/grants.py +0 -0
  138. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/heartbeat.py +0 -0
  139. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/home.py +0 -0
  140. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/household_router.py +0 -0
  141. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/housekeeping.py +0 -0
  142. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/identity.py +0 -0
  143. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/integration.py +0 -0
  144. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/key_exchange.py +0 -0
  145. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/mailbox.py +0 -0
  146. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/marketplace.py +0 -0
  147. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/mcp_server.py +0 -0
  148. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/metrics.py +0 -0
  149. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/models.py +0 -0
  150. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/node_registry.py +0 -0
  151. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/nostr_discovery.py +0 -0
  152. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/observability.py +0 -0
  153. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/operator_probe.py +0 -0
  154. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/outbox.py +0 -0
  155. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/outbox_migrate.py +0 -0
  156. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/padding.py +0 -0
  157. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/pairing.py +0 -0
  158. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/paths.py +0 -0
  159. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/peers.py +0 -0
  160. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/pq_provider.py +0 -0
  161. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/pqdm.py +0 -0
  162. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/pqkem.py +0 -0
  163. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/pqkem_backend.py +0 -0
  164. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/pqroute.py +0 -0
  165. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/pqroute_transport.py +0 -0
  166. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/pqsig.py +0 -0
  167. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/profile_router.py +0 -0
  168. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/public_pairing.py +0 -0
  169. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/pubsub.py +0 -0
  170. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/pubsub_transport.py +0 -0
  171. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/queue.py +0 -0
  172. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/ratelimit.py +0 -0
  173. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/realm.py +0 -0
  174. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/registry.py +0 -0
  175. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/router.py +0 -0
  176. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/signaling.py +0 -0
  177. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/signing.py +0 -0
  178. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/skfed_announce.py +0 -0
  179. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/skfed_directory.py +0 -0
  180. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/skfed_readdr.py +0 -0
  181. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/skfed_resolve.py +0 -0
  182. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/skworld_manifest.py +0 -0
  183. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/souls_router.py +0 -0
  184. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/ssrf.py +0 -0
  185. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/store_forward.py +0 -0
  186. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/syncthing_rail.py +0 -0
  187. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/tofu.py +0 -0
  188. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transport.py +0 -0
  189. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/__init__.py +0 -0
  190. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/audio_track.py +0 -0
  191. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/ble/__init__.py +0 -0
  192. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/ble/gatt.py +0 -0
  193. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/ble/identity.py +0 -0
  194. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/ble/node.py +0 -0
  195. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/ble/noise.py +0 -0
  196. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/ble/protocol.py +0 -0
  197. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/ble/radio.py +0 -0
  198. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/ble/relay.py +0 -0
  199. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/broker_server.py +0 -0
  200. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/file.py +0 -0
  201. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/http_s2s.py +0 -0
  202. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/lora/__init__.py +0 -0
  203. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/lora/addressing.py +0 -0
  204. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/lora/framing.py +0 -0
  205. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/lora/interface.py +0 -0
  206. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/lora/meshtastic_iface.py +0 -0
  207. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/lora/store.py +0 -0
  208. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/lora/transport.py +0 -0
  209. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/nostr.py +0 -0
  210. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/p2p_connector.py +0 -0
  211. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/p2p_manager.py +0 -0
  212. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/p2p_session.py +0 -0
  213. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/signaling_base.py +0 -0
  214. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/signaling_broker.py +0 -0
  215. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/signaling_mailbox.py +0 -0
  216. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/syncthing.py +0 -0
  217. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/tailscale.py +0 -0
  218. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/video_track.py +0 -0
  219. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/webrtc.py +0 -0
  220. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/webrtc_media.py +0 -0
  221. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/transports/websocket.py +0 -0
  222. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms/trustbackup.py +0 -0
  223. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms.egg-info/SOURCES.txt +0 -0
  224. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms.egg-info/dependency_links.txt +0 -0
  225. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms.egg-info/entry_points.txt +0 -0
  226. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms.egg-info/requires.txt +0 -0
  227. {skcomms-0.2.7 → skcomms-0.2.9}/src/skcomms.egg-info/top_level.txt +0 -0
  228. {skcomms-0.2.7 → skcomms-0.2.9}/tests/__init__.py +0 -0
  229. {skcomms-0.2.7 → skcomms-0.2.9}/tests/fixtures/pqdm2_from_dart.json +0 -0
  230. {skcomms-0.2.7 → skcomms-0.2.9}/tests/gen_pqdm2_fixture.py +0 -0
  231. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_access_exec.py +0 -0
  232. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_access_files.py +0 -0
  233. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_access_nonce_durable.py +0 -0
  234. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_access_rbac.py +0 -0
  235. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_access_routing.py +0 -0
  236. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_access_server.py +0 -0
  237. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_access_wiring.py +0 -0
  238. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_ack_bind.py +0 -0
  239. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_adapter_factory.py +0 -0
  240. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_adapter_fake.py +0 -0
  241. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_anon_queue.py +0 -0
  242. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_anon_transport.py +0 -0
  243. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_api_access_token.py +0 -0
  244. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_api_consent.py +0 -0
  245. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_api_consent_token_gate.py +0 -0
  246. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_api_cors.py +0 -0
  247. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_api_federation_inbox.py +0 -0
  248. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_api_geo_seed.py +0 -0
  249. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_api_geo_skcot_bridge.py +0 -0
  250. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_api_geo_units.py +0 -0
  251. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_api_health.py +0 -0
  252. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_api_send_signed_wire.py +0 -0
  253. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_api_skfed_directory.py +0 -0
  254. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_backfill_file_locations.py +0 -0
  255. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_ble_fragment.py +0 -0
  256. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_ble_gatt.py +0 -0
  257. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_ble_identity.py +0 -0
  258. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_ble_mesh.py +0 -0
  259. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_ble_noise.py +0 -0
  260. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_ble_pairing_bundle.py +0 -0
  261. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_ble_protocol.py +0 -0
  262. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_ble_radio.py +0 -0
  263. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_ble_relay.py +0 -0
  264. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_broker_server.py +0 -0
  265. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_capabilities.py +0 -0
  266. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_capauth_failclosed.py +0 -0
  267. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_capauth_key_reconcile.py +0 -0
  268. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_capauth_resolution.py +0 -0
  269. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_capauth_revoked_expired.py +0 -0
  270. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_channel_adapter.py +0 -0
  271. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_cluster_schema.py +0 -0
  272. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_config.py +0 -0
  273. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_consent.py +0 -0
  274. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_consent_ban_gate_fail_closed.py +0 -0
  275. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_consent_banfeeds.py +0 -0
  276. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_consent_captcha.py +0 -0
  277. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_consent_cli.py +0 -0
  278. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_consent_cli_mgmt.py +0 -0
  279. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_consent_greylist.py +0 -0
  280. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_consent_group_gate.py +0 -0
  281. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_consent_groups.py +0 -0
  282. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_consent_moderation.py +0 -0
  283. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_consent_pipeline.py +0 -0
  284. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_consent_policy.py +0 -0
  285. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_consent_requests.py +0 -0
  286. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_consent_runtime.py +0 -0
  287. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_consent_tiering.py +0 -0
  288. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_consent_tokens.py +0 -0
  289. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_core_inbound_beacon_skip.py +0 -0
  290. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_core_send_federated.py +0 -0
  291. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_core_send_signed.py +0 -0
  292. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_core_stop.py +0 -0
  293. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_crypto_failclosed.py +0 -0
  294. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_crypto_suites_q0.py +0 -0
  295. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_daemon_adapter_lifecycle.py +0 -0
  296. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_deadletter_ops.py +0 -0
  297. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_delivery_semantics.py +0 -0
  298. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_discord_adapter.py +0 -0
  299. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_encrypt_fail_closed.py +0 -0
  300. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_envelope_consent_token.py +0 -0
  301. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_envelope_v1.py +0 -0
  302. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_federation.py +0 -0
  303. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_federation_integration.py +0 -0
  304. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_geo_roundtrip_skcot.py +0 -0
  305. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_glossa_codebook.py +0 -0
  306. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_glossa_codec_l0.py +0 -0
  307. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_glossa_codec_l1.py +0 -0
  308. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_glossa_codec_l2.py +0 -0
  309. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_glossa_emergent.py +0 -0
  310. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_glossa_emergent_negotiator.py +0 -0
  311. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_glossa_emergent_protocol.py +0 -0
  312. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_glossa_gloss.py +0 -0
  313. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_glossa_handshake.py +0 -0
  314. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_glossa_handshake_lexicon.py +0 -0
  315. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_glossa_macros.py +0 -0
  316. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_glossa_macros_render.py +0 -0
  317. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_glossa_message.py +0 -0
  318. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_glossa_session.py +0 -0
  319. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_glossa_session_macros.py +0 -0
  320. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_glossa_to_human.py +0 -0
  321. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_grants.py +0 -0
  322. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_home_scaffold.py +0 -0
  323. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_housekeeping.py +0 -0
  324. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_http_s2s_star_recipient.py +0 -0
  325. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_http_s2s_transport.py +0 -0
  326. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_hybrid_envelope_sig.py +0 -0
  327. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_identity_env_override.py +0 -0
  328. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_identity_key_binding.py +0 -0
  329. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_identity_realm.py +0 -0
  330. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_identity_trust_backup.py +0 -0
  331. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_integration.py +0 -0
  332. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_key_exchange_transport_scoping.py +0 -0
  333. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_knowledge.py +0 -0
  334. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_lora_addressing.py +0 -0
  335. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_lora_framing.py +0 -0
  336. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_lora_interface.py +0 -0
  337. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_lora_meshtastic_iface.py +0 -0
  338. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_lora_store.py +0 -0
  339. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_lora_transport.py +0 -0
  340. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_mailbox.py +0 -0
  341. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_mailbox_encrypt_at_rest.py +0 -0
  342. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_mailbox_recipient_key.py +0 -0
  343. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_matrix_adapter.py +0 -0
  344. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_mode_b_setup.sh +0 -0
  345. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_no_subapp_dependency.py +0 -0
  346. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_node_registry.py +0 -0
  347. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_nonce_durable.py +0 -0
  348. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_nostr_discovery.py +0 -0
  349. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_observability.py +0 -0
  350. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_operator_cli.py +0 -0
  351. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_outbox_home_alignment.py +0 -0
  352. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_outbox_migrate.py +0 -0
  353. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_p2p_connector.py +0 -0
  354. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_p2p_manager.py +0 -0
  355. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_p2p_media.py +0 -0
  356. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_p2p_session.py +0 -0
  357. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_padding.py +0 -0
  358. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_pairing.py +0 -0
  359. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_pairing_cli.py +0 -0
  360. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_paths_scoping.py +0 -0
  361. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_peer_inbox_traversal.py +0 -0
  362. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_peers.py +0 -0
  363. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_pqc_cutover.py +0 -0
  364. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_pqc_shim.py +0 -0
  365. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_pqdm.py +0 -0
  366. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_pqdm2.py +0 -0
  367. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_pqdm2_interop.py +0 -0
  368. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_pqkem.py +0 -0
  369. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_pqroute.py +0 -0
  370. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_pqroute_padding.py +0 -0
  371. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_pqroute_transport.py +0 -0
  372. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_pqsig.py +0 -0
  373. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_prune_inbox.py +0 -0
  374. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_public_pairing.py +0 -0
  375. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_queue_bounds.py +0 -0
  376. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_receiver_wire_ttl_expiry.py +0 -0
  377. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_registry.py +0 -0
  378. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_registry_cli.py +0 -0
  379. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_router_failure_log_dedup.py +0 -0
  380. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_router_perm_backoff.py +0 -0
  381. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_router_perm_backoff_fixes.py +0 -0
  382. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_router_quarantine.py +0 -0
  383. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_router_rail_ordering.py +0 -0
  384. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_router_route_anon.py +0 -0
  385. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_router_route_signed.py +0 -0
  386. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_router_star_broadcast_routing.py +0 -0
  387. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_router_unsigned_excludes_https_s2s.py +0 -0
  388. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_sign_fail_closed.py +0 -0
  389. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_signaling_broker.py +0 -0
  390. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_signaling_mailbox.py +0 -0
  391. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_signaling_select.py +0 -0
  392. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_skfed_announce.py +0 -0
  393. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_skfed_cli.py +0 -0
  394. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_skfed_directory.py +0 -0
  395. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_skfed_dns.sh +0 -0
  396. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_skfed_operator_pin.py +0 -0
  397. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_skfed_readdr.py +0 -0
  398. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_skfed_resolve.py +0 -0
  399. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_skfed_ssrf.py +0 -0
  400. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_skworld_manifest.py +0 -0
  401. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_slack_adapter.py +0 -0
  402. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_smoke.py +0 -0
  403. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_ssrf_guard.py +0 -0
  404. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_store_forward.py +0 -0
  405. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_syncthing_rail.py +0 -0
  406. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_syncthing_recipient_guard.py +0 -0
  407. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_telegram_adapter.py +0 -0
  408. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_tofu.py +0 -0
  409. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_typed_message_content.py +0 -0
  410. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_webrtc_bridge_lifecycle.py +0 -0
  411. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_webrtc_ice_logging.py +0 -0
  412. {skcomms-0.2.7 → skcomms-0.2.9}/tests/test_webrtc_signaling_log_dedup.py +0 -0
@@ -0,0 +1,61 @@
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
+ # Run the gitleaks BINARY, not gitleaks-action.
32
+ #
33
+ # The action requires a paid license for ORGANIZATION-owned repos, and
34
+ # this repo is owned by the smilinTux org, so every run died with
35
+ # "missing gitleaks license" BEFORE scanning anything. That is worse than
36
+ # having no gate: the check sat red on main for months, so it read as
37
+ # known-broken and got ignored, while the repo had no secret scanning at
38
+ # all. It also hid a real (benign) finding, see .gitleaks.toml.
39
+ #
40
+ # gitleaks itself is MIT-licensed and free. Only the ACTION wrapper is
41
+ # licensed, so invoking the binary directly restores real scanning at no
42
+ # cost and with no org secret to provision. Pinned to an exact version so
43
+ # a new upstream rule cannot turn CI red without a deliberate bump.
44
+ - name: Install gitleaks
45
+ env:
46
+ GITLEAKS_VERSION: 8.28.0
47
+ run: |
48
+ set -euo pipefail
49
+ curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \
50
+ | tar -xz -C /usr/local/bin gitleaks
51
+ gitleaks version
52
+ - name: Run gitleaks
53
+ # --redact so a genuine finding never echoes the secret into public CI
54
+ # logs. --exit-code 1 makes a finding fail the build.
55
+ run: |
56
+ gitleaks detect \
57
+ --source . \
58
+ --config .gitleaks.toml \
59
+ --redact \
60
+ --no-banner \
61
+ --exit-code 1
@@ -33,3 +33,15 @@ docs/superpowers/specs/2026-06-13-ble-mesh-proximity-transport-design.md:178.
33
33
  """
34
34
  paths = ['''docs/superpowers/specs/2026-06-13-ble-mesh-proximity-transport-design\.md''']
35
35
  regexes = ['''Ed25519-signed''']
36
+
37
+ # Fake PGP key blocks in the identity-trust backup test. The private-key rule
38
+ # matches the "-----BEGIN PGP PRIVATE KEY BLOCK-----" armor header, but the body
39
+ # is the literal string "fake-private" (and "fake-public"): a fixture that
40
+ # exercises the backup/restore path without any real key material. Confirmed
41
+ # non-secret on 2026-08-14 by reading tests/test_identity_trust_backup.py:42-43.
42
+ #
43
+ # This is the finding that had been sitting undetected: the CI gate has never
44
+ # actually scanned anything (see secret-scan.yml), so nothing ever reported it.
45
+ [[allowlists]]
46
+ description = "Fake PGP armor fixtures in tests/test_identity_trust_backup.py"
47
+ paths = ['''tests/test_identity_trust_backup\.py''']
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: skcomms
3
- Version: 0.2.7
3
+ Version: 0.2.9
4
4
  Summary: Sovereign communications for AI agents — multi-channel transport + FQID realm addressing
5
5
  Author-email: smilinTux <chefboyrdave2.1@gmail.com>
6
6
  License: GPL-3.0-or-later
@@ -55,15 +55,36 @@ def _base() -> Optional[str]:
55
55
  def mirror_pairing(fqid: str, pubkey: str) -> None:
56
56
  """Record an accepted peer as a TOFU enrollment in capauth.pairing.
57
57
 
58
+ Idempotent per (subject, key): re-mirroring an already-enrolled key is a
59
+ no-op. Without that guard every accept minted ANOTHER approved DeviceRecord,
60
+ and because each accept presents a freshly generated key, TOFU pinned
61
+ nothing: the subject simply accumulated approved devices. Observed on
62
+ ``opus@chef.skworld``, 22 records in one afternoon, every one a distinct
63
+ fingerprint, in pairs because both :func:`skcomms.pairing.accept_pairing`
64
+ and the public-pairing path mirror the same accept.
65
+
66
+ Enrollments are NOT deduped by content upstream (``enroll_device`` +
67
+ ``approve`` always create), so the check belongs here at the call site.
68
+
58
69
  Best-effort: gated on the kernel flag, and any capauth error is logged at
59
70
  debug and swallowed so it can never break :func:`accept_pairing`.
60
71
  """
61
72
  if not kernel_enabled() or not fqid or not pubkey:
62
73
  return
63
74
  try:
64
- from capauth.pairing import approve, enroll_device
75
+ from capauth.pairing import approve, enroll_device, list_devices
65
76
 
66
77
  base = _base()
78
+
79
+ # Trust on FIRST use: if this subject already has a live device for this
80
+ # key, keep the existing record rather than minting a second one. A
81
+ # revoked record is deliberately not resurrected here; re-enrolling a
82
+ # revoked key must be an explicit operator action, not a side effect of
83
+ # a peer re-accept.
84
+ if _already_enrolled(fqid, pubkey, base):
85
+ logger.debug("pairing mirror: %s already enrolled for this key, skipping", fqid)
86
+ return
87
+
67
88
  enr = enroll_device(
68
89
  pubkey,
69
90
  list(_DEFAULT_SCOPES),
@@ -76,6 +97,30 @@ def mirror_pairing(fqid: str, pubkey: str) -> None:
76
97
  logger.debug("capauth pairing mirror failed", exc_info=True)
77
98
 
78
99
 
100
+ def _already_enrolled(fqid: str, pubkey: str, base) -> bool:
101
+ """Whether *fqid* already has a live capauth device carrying *pubkey*.
102
+
103
+ Compares the armored public key stored on the DeviceRecord. A record with
104
+ no stored key never matches, so it enrolls rather than silently treating an
105
+ unknown key as already trusted. Any lookup failure also returns False: a
106
+ duplicate record is far better than dropping a real pairing.
107
+ """
108
+ from capauth.pairing import list_devices
109
+
110
+ try:
111
+ existing = list_devices(fqid, base_dir=base, include_revoked=False)
112
+ except Exception:
113
+ logger.debug("pairing mirror: device lookup failed, enrolling anyway", exc_info=True)
114
+ return False
115
+
116
+ presented = (pubkey or "").strip()
117
+ for dev in existing:
118
+ stored = (getattr(dev, "pubkey", None) or "").strip()
119
+ if stored and stored == presented:
120
+ return True
121
+ return False
122
+
123
+
79
124
  def mirror_revocation(fqid: str) -> None:
80
125
  """Revoke every non-revoked capauth device whose subject is this peer's fqid.
81
126
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: skcomms
3
- Version: 0.2.7
3
+ Version: 0.2.9
4
4
  Summary: Sovereign communications for AI agents — multi-channel transport + FQID realm addressing
5
5
  Author-email: smilinTux <chefboyrdave2.1@gmail.com>
6
6
  License: GPL-3.0-or-later
@@ -25,8 +25,19 @@ def _isolate_skcomms_home(tmp_path, monkeypatch):
25
25
  isolation, tests that build servers with defaults would write replay
26
26
  state into the operator's live home. Tests that need a specific home
27
27
  still win: their own ``monkeypatch.setenv`` runs after this fixture.
28
+
29
+ The pairing kernel base is isolated for the same reason, and it is the
30
+ sharper case: ``mirror_pairing`` enrolls + approves into the capauth
31
+ pairing store, which is the store ``capauth.authz.decide()`` reads to
32
+ authorize. Four test files exercise pairing without setting the base
33
+ themselves (test_pairing, test_pairing_cli, test_public_pairing,
34
+ test_key_exchange_transport_scoping), so a plain ``pytest tests/`` was
35
+ minting real approved DeviceRecords into the operator's live security
36
+ store. Measured: running test_public_pairing.py alone added 2 records,
37
+ and 58 accumulated records for ``opus@chef.skworld`` trace to this.
28
38
  """
29
39
  monkeypatch.setenv("SKCOMMS_HOME", str(tmp_path / "skcomms-home"))
40
+ monkeypatch.setenv("SKCOMMS_PAIRING_KERNEL_BASE", str(tmp_path / "capauth-pairing"))
30
41
  # Nonce caches are node-local (outside the synced home) by default; keep
31
42
  # tests off the operator's real ~/.local/state/skcomms as well.
32
43
  monkeypatch.setenv("SKCOMMS_NONCE_CACHE_DIR", str(tmp_path / "skcomms-local-state"))
@@ -308,3 +308,54 @@ def test_public_pairing_kernel_disabled_writes_nothing(tmp_path, monkeypatch):
308
308
  )
309
309
  assert res["paired"]["fqid"] == "opus@chef.skworld"
310
310
  assert list_devices(subject="opus@chef.skworld", base_dir=str(tmp_path / "capauth")) == []
311
+
312
+
313
+ # ── idempotency (trust on FIRST use, not on every use) ───────────────────
314
+
315
+
316
+ # Deliberately NOT shaped like real base64 key material. mirror_pairing only
317
+ # needs two distinct opaque strings (it stores them and fingerprints them), and
318
+ # a realistic-looking prefix made the secret scanner flag these as a leaked
319
+ # key. Suppressing that with an allowlist entry would blunt a gate that only
320
+ # started working on 2026-08-14 (see the secret-scan workflow), so the fixture
321
+ # avoids tripping it in the first place.
322
+ _KEY_A = "not-a-real-key-fixture-alpha"
323
+ _KEY_B = "not-a-real-key-fixture-bravo"
324
+
325
+
326
+ def test_re_mirroring_the_same_key_does_not_mint_a_second_device(tmp_path, monkeypatch):
327
+ """Repeated accepts of one peer+key must converge on ONE device record.
328
+
329
+ Regression pin for the observed drift: opus@chef.skworld accumulated 22
330
+ approved TOFU records in a single afternoon because every accept called
331
+ enroll_device + approve unconditionally.
332
+ """
333
+ monkeypatch.setenv("SKCOMMS_PAIRING_KERNEL_BASE", str(tmp_path))
334
+ from capauth.pairing.kernel import list_devices
335
+
336
+ from skcomms.pairing_mirror import mirror_pairing
337
+
338
+ for _ in range(5):
339
+ mirror_pairing("peer@example.skworld", _KEY_A)
340
+
341
+ devices = list_devices("peer@example.skworld", base_dir=tmp_path, include_revoked=True)
342
+ assert len(devices) == 1
343
+
344
+
345
+ def test_a_genuinely_new_key_still_enrolls(tmp_path, monkeypatch):
346
+ """The guard must not block a real second device for the same subject."""
347
+ monkeypatch.setenv("SKCOMMS_PAIRING_KERNEL_BASE", str(tmp_path))
348
+ from capauth.pairing.kernel import list_devices
349
+
350
+ from skcomms.pairing_mirror import mirror_pairing
351
+
352
+ mirror_pairing("peer@example.skworld", _KEY_A)
353
+ mirror_pairing("peer@example.skworld", _KEY_B)
354
+
355
+ devices = list_devices("peer@example.skworld", base_dir=tmp_path, include_revoked=True)
356
+ assert len(devices) == 2
357
+
358
+ # And re-presenting the first key still does not add a third.
359
+ mirror_pairing("peer@example.skworld", _KEY_A)
360
+ devices = list_devices("peer@example.skworld", base_dir=tmp_path, include_revoked=True)
361
+ assert len(devices) == 2
@@ -1,48 +0,0 @@
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 }}
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes