skcomms 0.2.0__tar.gz → 0.2.1__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 (334) hide show
  1. {skcomms-0.2.0 → skcomms-0.2.1}/PKG-INFO +1 -1
  2. {skcomms-0.2.0 → skcomms-0.2.1}/pyproject.toml +1 -1
  3. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/access/server.py +3 -0
  4. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/ack.py +85 -18
  5. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/api.py +333 -57
  6. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/capauth_validator.py +108 -27
  7. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/cli.py +474 -0
  8. skcomms-0.2.1/src/skcomms/config.py +415 -0
  9. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/consent_group_gate.py +27 -14
  10. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/consent_pipeline.py +52 -18
  11. skcomms-0.2.1/src/skcomms/core.py +1428 -0
  12. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/crypto.py +11 -1
  13. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/discovery.py +24 -7
  14. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/federation.py +85 -1
  15. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/home.py +47 -6
  16. skcomms-0.2.1/src/skcomms/housekeeping.py +260 -0
  17. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/key_exchange.py +19 -8
  18. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/mailbox.py +276 -41
  19. skcomms-0.2.1/src/skcomms/observability.py +398 -0
  20. skcomms-0.2.1/src/skcomms/outbox.py +1117 -0
  21. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/outbox_migrate.py +154 -0
  22. skcomms-0.2.1/src/skcomms/paths.py +405 -0
  23. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/queue.py +23 -6
  24. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/registry.py +10 -4
  25. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/router.py +475 -184
  26. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/signaling.py +37 -7
  27. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/signing.py +11 -0
  28. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/skfed_resolve.py +28 -86
  29. skcomms-0.2.1/src/skcomms/ssrf.py +398 -0
  30. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/store_forward.py +31 -11
  31. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/tofu.py +59 -0
  32. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transport.py +32 -1
  33. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/broker_server.py +9 -4
  34. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/file.py +295 -15
  35. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/http_s2s.py +189 -15
  36. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/syncthing.py +104 -1
  37. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/webrtc.py +61 -5
  38. skcomms-0.2.1/src/skcomms/trustbackup.py +549 -0
  39. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms.egg-info/PKG-INFO +1 -1
  40. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms.egg-info/SOURCES.txt +31 -14
  41. skcomms-0.2.1/tests/test_ack_bind.py +380 -0
  42. skcomms-0.2.1/tests/test_api_cors.py +105 -0
  43. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_api_federation_inbox.py +31 -9
  44. skcomms-0.2.1/tests/test_api_health.py +97 -0
  45. skcomms-0.2.1/tests/test_api_send_signed_wire.py +247 -0
  46. skcomms-0.2.1/tests/test_broker_server.py +92 -0
  47. skcomms-0.2.1/tests/test_capauth_failclosed.py +249 -0
  48. skcomms-0.2.1/tests/test_consent_ban_gate_fail_closed.py +212 -0
  49. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_consent_group_gate.py +111 -14
  50. skcomms-0.2.1/tests/test_core_inbound_beacon_skip.py +81 -0
  51. skcomms-0.2.1/tests/test_core_send_signed.py +251 -0
  52. skcomms-0.2.1/tests/test_deadletter_ops.py +481 -0
  53. skcomms-0.2.1/tests/test_delivery_semantics.py +268 -0
  54. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_home_scaffold.py +62 -0
  55. skcomms-0.2.1/tests/test_housekeeping.py +552 -0
  56. skcomms-0.2.1/tests/test_http_s2s_star_recipient.py +39 -0
  57. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_http_s2s_transport.py +149 -9
  58. skcomms-0.2.1/tests/test_identity_trust_backup.py +639 -0
  59. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_mailbox.py +24 -17
  60. skcomms-0.2.1/tests/test_mailbox_encrypt_at_rest.py +516 -0
  61. skcomms-0.2.1/tests/test_nonce_durable.py +258 -0
  62. skcomms-0.2.1/tests/test_observability.py +492 -0
  63. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_outbox_migrate.py +126 -11
  64. skcomms-0.2.1/tests/test_paths_scoping.py +536 -0
  65. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_pqroute_padding.py +2 -3
  66. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_pqroute_transport.py +2 -3
  67. skcomms-0.2.1/tests/test_prune_inbox.py +96 -0
  68. skcomms-0.2.1/tests/test_queue_bounds.py +790 -0
  69. skcomms-0.2.1/tests/test_receiver_wire_ttl_expiry.py +50 -0
  70. skcomms-0.2.1/tests/test_router_failure_log_dedup.py +174 -0
  71. skcomms-0.2.1/tests/test_router_perm_backoff.py +131 -0
  72. skcomms-0.2.1/tests/test_router_perm_backoff_fixes.py +156 -0
  73. skcomms-0.2.1/tests/test_router_quarantine.py +122 -0
  74. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_router_rail_ordering.py +28 -23
  75. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_router_route_anon.py +2 -1
  76. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_router_route_signed.py +3 -2
  77. skcomms-0.2.1/tests/test_router_star_broadcast_routing.py +139 -0
  78. skcomms-0.2.1/tests/test_router_unsigned_excludes_https_s2s.py +155 -0
  79. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_skfed_ssrf.py +20 -9
  80. skcomms-0.2.1/tests/test_ssrf_guard.py +431 -0
  81. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_store_forward.py +92 -0
  82. skcomms-0.2.1/tests/test_webrtc_signaling_log_dedup.py +54 -0
  83. skcomms-0.2.0/src/skcomms/config.py +0 -230
  84. skcomms-0.2.0/src/skcomms/core.py +0 -947
  85. skcomms-0.2.0/src/skcomms/cot.py +0 -299
  86. skcomms-0.2.0/src/skcomms/cot_agent.py +0 -173
  87. skcomms-0.2.0/src/skcomms/cot_client.py +0 -142
  88. skcomms-0.2.0/src/skcomms/cot_pki.py +0 -622
  89. skcomms-0.2.0/src/skcomms/cot_server.py +0 -603
  90. skcomms-0.2.0/src/skcomms/cot_service.py +0 -191
  91. skcomms-0.2.0/src/skcomms/geo.py +0 -477
  92. skcomms-0.2.0/src/skcomms/outbox.py +0 -663
  93. skcomms-0.2.0/tests/test_ack_bind.py +0 -89
  94. skcomms-0.2.0/tests/test_broker_server.py +0 -36
  95. skcomms-0.2.0/tests/test_capauth_failclosed.py +0 -98
  96. skcomms-0.2.0/tests/test_cot_agent_nameagnostic.py +0 -18
  97. skcomms-0.2.0/tests/test_cot_beacon_outbox.py +0 -204
  98. skcomms-0.2.0/tests/test_cot_codec.py +0 -146
  99. skcomms-0.2.0/tests/test_cot_pki.py +0 -150
  100. skcomms-0.2.0/tests/test_cot_server.py +0 -216
  101. skcomms-0.2.0/tests/test_geo.py +0 -247
  102. skcomms-0.2.0/tests/test_mailbox_encrypt_at_rest.py +0 -173
  103. {skcomms-0.2.0 → skcomms-0.2.1}/LICENSE +0 -0
  104. {skcomms-0.2.0 → skcomms-0.2.1}/README.md +0 -0
  105. {skcomms-0.2.0 → skcomms-0.2.1}/setup.cfg +0 -0
  106. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/__init__.py +0 -0
  107. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/access/__init__.py +0 -0
  108. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/access/audit.py +0 -0
  109. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/access/config.py +0 -0
  110. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/access/exec.py +0 -0
  111. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/access/files.py +0 -0
  112. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/access/grants.py +0 -0
  113. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/access/knowledge.py +0 -0
  114. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/access/registry.py +0 -0
  115. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/access/routing.py +0 -0
  116. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/access/wiring.py +0 -0
  117. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/adapters/__init__.py +0 -0
  118. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/adapters/base.py +0 -0
  119. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/adapters/discord.py +0 -0
  120. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/adapters/factory.py +0 -0
  121. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/adapters/fake.py +0 -0
  122. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/adapters/matrix.py +0 -0
  123. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/adapters/models.py +0 -0
  124. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/adapters/registry.py +0 -0
  125. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/adapters/slack.py +0 -0
  126. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/adapters/telegram.py +0 -0
  127. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/anon_queue.py +0 -0
  128. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/anon_transport.py +0 -0
  129. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/capabilities.py +0 -0
  130. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/cluster.py +0 -0
  131. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/compression.py +0 -0
  132. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/consent.py +0 -0
  133. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/consent_banfeeds.py +0 -0
  134. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/consent_captcha.py +0 -0
  135. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/consent_greylist.py +0 -0
  136. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/consent_groups.py +0 -0
  137. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/consent_moderation.py +0 -0
  138. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/consent_policy.py +0 -0
  139. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/consent_requests.py +0 -0
  140. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/consent_runtime.py +0 -0
  141. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/consent_tiering.py +0 -0
  142. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/consent_tokens.py +0 -0
  143. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/crypto_suites.py +0 -0
  144. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/did_router.py +0 -0
  145. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/envelope.py +0 -0
  146. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/glossa/__init__.py +0 -0
  147. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/glossa/codebook.py +0 -0
  148. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/glossa/codec.py +0 -0
  149. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/glossa/emergent.py +0 -0
  150. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/glossa/gloss.py +0 -0
  151. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/glossa/handshake.py +0 -0
  152. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/glossa/macros.py +0 -0
  153. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/glossa/message.py +0 -0
  154. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/glossa/session.py +0 -0
  155. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/grants.py +0 -0
  156. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/heartbeat.py +0 -0
  157. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/household_router.py +0 -0
  158. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/identity.py +0 -0
  159. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/integration.py +0 -0
  160. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/marketplace.py +0 -0
  161. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/mcp_server.py +0 -0
  162. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/metrics.py +0 -0
  163. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/models.py +0 -0
  164. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/node_registry.py +0 -0
  165. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/nostr_discovery.py +0 -0
  166. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/padding.py +0 -0
  167. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/pairing.py +0 -0
  168. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/peers.py +0 -0
  169. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/pq_provider.py +0 -0
  170. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/pqdm.py +0 -0
  171. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/pqkem.py +0 -0
  172. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/pqkem_backend.py +0 -0
  173. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/pqroute.py +0 -0
  174. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/pqroute_transport.py +0 -0
  175. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/pqsig.py +0 -0
  176. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/profile_router.py +0 -0
  177. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/pubsub.py +0 -0
  178. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/pubsub_transport.py +0 -0
  179. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/ratelimit.py +0 -0
  180. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/realm.py +0 -0
  181. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/skfed_announce.py +0 -0
  182. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/skfed_directory.py +0 -0
  183. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/skfed_readdr.py +0 -0
  184. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/souls_router.py +0 -0
  185. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/__init__.py +0 -0
  186. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/audio_track.py +0 -0
  187. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/ble/__init__.py +0 -0
  188. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/ble/gatt.py +0 -0
  189. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/ble/identity.py +0 -0
  190. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/ble/node.py +0 -0
  191. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/ble/noise.py +0 -0
  192. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/ble/protocol.py +0 -0
  193. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/ble/radio.py +0 -0
  194. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/ble/relay.py +0 -0
  195. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/lora/__init__.py +0 -0
  196. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/lora/addressing.py +0 -0
  197. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/lora/framing.py +0 -0
  198. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/lora/interface.py +0 -0
  199. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/lora/meshtastic_iface.py +0 -0
  200. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/lora/store.py +0 -0
  201. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/lora/transport.py +0 -0
  202. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/nostr.py +0 -0
  203. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/p2p_connector.py +0 -0
  204. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/p2p_manager.py +0 -0
  205. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/p2p_session.py +0 -0
  206. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/signaling_base.py +0 -0
  207. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/signaling_broker.py +0 -0
  208. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/signaling_mailbox.py +0 -0
  209. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/tailscale.py +0 -0
  210. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/video_track.py +0 -0
  211. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/webrtc_media.py +0 -0
  212. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms/transports/websocket.py +0 -0
  213. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms.egg-info/dependency_links.txt +0 -0
  214. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms.egg-info/entry_points.txt +0 -0
  215. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms.egg-info/requires.txt +0 -0
  216. {skcomms-0.2.0 → skcomms-0.2.1}/src/skcomms.egg-info/top_level.txt +0 -0
  217. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_access_exec.py +0 -0
  218. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_access_files.py +0 -0
  219. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_access_rbac.py +0 -0
  220. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_access_routing.py +0 -0
  221. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_access_server.py +0 -0
  222. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_access_wiring.py +0 -0
  223. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_adapter_factory.py +0 -0
  224. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_adapter_fake.py +0 -0
  225. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_anon_queue.py +0 -0
  226. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_anon_transport.py +0 -0
  227. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_api_access_token.py +0 -0
  228. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_api_consent.py +0 -0
  229. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_api_consent_token_gate.py +0 -0
  230. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_api_skfed_directory.py +0 -0
  231. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_backfill_file_locations.py +0 -0
  232. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_ble_fragment.py +0 -0
  233. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_ble_gatt.py +0 -0
  234. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_ble_identity.py +0 -0
  235. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_ble_mesh.py +0 -0
  236. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_ble_noise.py +0 -0
  237. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_ble_pairing_bundle.py +0 -0
  238. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_ble_protocol.py +0 -0
  239. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_ble_radio.py +0 -0
  240. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_ble_relay.py +0 -0
  241. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_capabilities.py +0 -0
  242. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_capauth_key_reconcile.py +0 -0
  243. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_channel_adapter.py +0 -0
  244. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_config.py +0 -0
  245. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_consent.py +0 -0
  246. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_consent_banfeeds.py +0 -0
  247. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_consent_captcha.py +0 -0
  248. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_consent_cli.py +0 -0
  249. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_consent_cli_mgmt.py +0 -0
  250. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_consent_greylist.py +0 -0
  251. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_consent_groups.py +0 -0
  252. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_consent_moderation.py +0 -0
  253. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_consent_pipeline.py +0 -0
  254. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_consent_policy.py +0 -0
  255. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_consent_requests.py +0 -0
  256. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_consent_runtime.py +0 -0
  257. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_consent_tiering.py +0 -0
  258. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_consent_tokens.py +0 -0
  259. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_core_send_federated.py +0 -0
  260. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_crypto_failclosed.py +0 -0
  261. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_crypto_suites_q0.py +0 -0
  262. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_daemon_adapter_lifecycle.py +0 -0
  263. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_discord_adapter.py +0 -0
  264. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_encrypt_fail_closed.py +0 -0
  265. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_envelope_consent_token.py +0 -0
  266. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_envelope_v1.py +0 -0
  267. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_federation.py +0 -0
  268. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_federation_integration.py +0 -0
  269. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_glossa_codebook.py +0 -0
  270. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_glossa_codec_l0.py +0 -0
  271. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_glossa_codec_l1.py +0 -0
  272. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_glossa_codec_l2.py +0 -0
  273. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_glossa_emergent.py +0 -0
  274. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_glossa_emergent_negotiator.py +0 -0
  275. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_glossa_emergent_protocol.py +0 -0
  276. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_glossa_gloss.py +0 -0
  277. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_glossa_handshake.py +0 -0
  278. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_glossa_handshake_lexicon.py +0 -0
  279. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_glossa_macros.py +0 -0
  280. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_glossa_macros_render.py +0 -0
  281. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_glossa_message.py +0 -0
  282. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_glossa_session.py +0 -0
  283. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_glossa_session_macros.py +0 -0
  284. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_glossa_to_human.py +0 -0
  285. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_grants.py +0 -0
  286. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_hybrid_envelope_sig.py +0 -0
  287. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_identity_env_override.py +0 -0
  288. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_identity_key_binding.py +0 -0
  289. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_identity_realm.py +0 -0
  290. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_integration.py +0 -0
  291. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_knowledge.py +0 -0
  292. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_lora_addressing.py +0 -0
  293. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_lora_framing.py +0 -0
  294. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_lora_interface.py +0 -0
  295. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_lora_meshtastic_iface.py +0 -0
  296. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_lora_store.py +0 -0
  297. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_lora_transport.py +0 -0
  298. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_matrix_adapter.py +0 -0
  299. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_node_registry.py +0 -0
  300. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_nostr_discovery.py +0 -0
  301. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_p2p_connector.py +0 -0
  302. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_p2p_manager.py +0 -0
  303. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_p2p_media.py +0 -0
  304. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_p2p_session.py +0 -0
  305. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_padding.py +0 -0
  306. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_pairing.py +0 -0
  307. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_pairing_cli.py +0 -0
  308. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_peer_inbox_traversal.py +0 -0
  309. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_peers.py +0 -0
  310. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_pqc_cutover.py +0 -0
  311. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_pqc_shim.py +0 -0
  312. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_pqdm.py +0 -0
  313. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_pqkem.py +0 -0
  314. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_pqroute.py +0 -0
  315. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_pqsig.py +0 -0
  316. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_registry.py +0 -0
  317. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_registry_cli.py +0 -0
  318. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_sign_fail_closed.py +0 -0
  319. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_signaling_broker.py +0 -0
  320. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_signaling_mailbox.py +0 -0
  321. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_signaling_select.py +0 -0
  322. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_skfed_announce.py +0 -0
  323. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_skfed_cli.py +0 -0
  324. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_skfed_directory.py +0 -0
  325. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_skfed_operator_pin.py +0 -0
  326. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_skfed_readdr.py +0 -0
  327. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_skfed_resolve.py +0 -0
  328. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_slack_adapter.py +0 -0
  329. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_smoke.py +0 -0
  330. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_syncthing_recipient_guard.py +0 -0
  331. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_telegram_adapter.py +0 -0
  332. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_tofu.py +0 -0
  333. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_typed_message_content.py +0 -0
  334. {skcomms-0.2.0 → skcomms-0.2.1}/tests/test_webrtc_ice_logging.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: skcomms
3
- Version: 0.2.0
3
+ Version: 0.2.1
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "skcomms"
7
- version = "0.2.0"
7
+ version = "0.2.1"
8
8
  description = "Sovereign communications for AI agents — multi-channel transport + FQID realm addressing"
9
9
  readme = "README.md"
10
10
  license = {text = "GPL-3.0-or-later"}
@@ -118,6 +118,9 @@ class AccessServer:
118
118
  self.registry = registry or DEFAULT_REGISTRY
119
119
  self._peer_store = peer_store if peer_store is not None else PeerStore()
120
120
  self.verifier = verifier or self._build_verifier()
121
+ # KNOWN GAP (follow-up coord f465b407): in-memory cache means a
122
+ # daemon restart reopens a replay window on the sk-access surface.
123
+ # Migrate to the durable per-node store (see api._get_nonce_cache).
121
124
  self.nonce_cache = fed.NonceCache()
122
125
  self.audit = audit or AccessAuditLog(node=self.config.node_name)
123
126
  self._register_builtins()
@@ -20,7 +20,7 @@ import logging
20
20
  from datetime import datetime, timezone
21
21
  from enum import Enum
22
22
  from pathlib import Path
23
- from typing import Optional
23
+ from typing import Callable, Optional
24
24
 
25
25
  from pydantic import BaseModel, Field
26
26
 
@@ -81,16 +81,27 @@ class AckTracker:
81
81
  Args:
82
82
  acks_dir: Directory for ACK tracking files.
83
83
  default_timeout: Default seconds to wait for ACK.
84
+ sender_verifier: Optional callable authenticating an ACK envelope
85
+ against its claimed sender (CapAuth/PGP signature). Fails closed:
86
+ a verifier that returns falsy or raises rejects the ACK.
84
87
  """
85
88
 
86
89
  def __init__(
87
90
  self,
88
91
  acks_dir: Optional[Path] = None,
89
92
  default_timeout: int = DEFAULT_ACK_TIMEOUT,
93
+ sender_verifier: Optional[Callable[[MessageEnvelope], bool]] = None,
90
94
  ):
91
95
  self._dir = acks_dir or Path(SKCOMMS_HOME).expanduser() / ACKS_DIR_NAME
92
96
  self._dir.mkdir(parents=True, exist_ok=True)
93
97
  self._default_timeout = default_timeout
98
+ # Optional cryptographic authenticator for inbound ACKs. Given the ACK
99
+ # envelope, returns True iff the ACK is authenticated as coming from its
100
+ # claimed sender (e.g. its CapAuth/PGP payload signature verifies). When
101
+ # wired in, string identity alone is not trusted. Reuses the existing
102
+ # signing stack (skcomms.crypto.EnvelopeCrypto.verify_signature), no new
103
+ # crypto scheme.
104
+ self._sender_verifier = sender_verifier
94
105
 
95
106
  @property
96
107
  def acks_dir(self) -> Path:
@@ -124,16 +135,51 @@ class AckTracker:
124
135
  logger.debug("Tracking ACK for %s -> %s", envelope.envelope_id[:8], envelope.recipient)
125
136
  return pending
126
137
 
138
+ @staticmethod
139
+ def _identity_matches(a: str, b: str) -> bool:
140
+ """Compare two identities (agent name, FQID, or PGP fingerprint).
141
+
142
+ Whitespace- and case-insensitive: a PGP fingerprint may be stored
143
+ spaced or unspaced as it crosses different transports. Empty values
144
+ never match (fail closed).
145
+
146
+ Args:
147
+ a: First identity string.
148
+ b: Second identity string.
149
+
150
+ Returns:
151
+ True if both are non-empty and equal after normalization.
152
+ """
153
+ if not a or not b:
154
+ return False
155
+ return a.replace(" ", "").casefold() == b.replace(" ", "").casefold()
156
+
127
157
  def process_ack(self, ack_envelope: MessageEnvelope) -> Optional[PendingAck]:
128
158
  """Process a received ACK envelope and resolve the pending entry.
129
159
 
130
- The ACK's content holds the original envelope_id.
160
+ The ACK's content holds the original envelope_id. Before an ACK is
161
+ allowed to confirm delivery it is bound to the sender identity and
162
+ checked for replay:
163
+
164
+ * Sender binding (anti-forgery): only the party the original message
165
+ was delivered to (``pending.recipient``) may acknowledge it. An ACK
166
+ whose ``sender`` is not that recipient is rejected, so a third party
167
+ cannot forge a delivery confirmation just by learning the
168
+ ``envelope_id``.
169
+ * Cryptographic authentication (optional): when a ``sender_verifier``
170
+ is wired in, the ACK's CapAuth/PGP signature must authenticate its
171
+ claimed sender; string identity alone is not trusted. Fails closed.
172
+ * Replay / stale rejection: an ACK may confirm a pending entry exactly
173
+ once. A duplicate ACK for an already-confirmed message, or a stale
174
+ ACK replayed after the entry timed out, does not mutate state (no
175
+ re-confirmation, no resurrection).
131
176
 
132
177
  Args:
133
178
  ack_envelope: A received ACK-type envelope.
134
179
 
135
180
  Returns:
136
- The resolved PendingAck, or None if no matching pending found.
181
+ The resolved PendingAck, or None if the ACK is unknown, forged,
182
+ unauthenticated, or a replay.
137
183
  """
138
184
  if not ack_envelope.is_ack:
139
185
  return None
@@ -142,7 +188,9 @@ class AckTracker:
142
188
  path = self._dir / f"{original_id}{ACK_SUFFIX}"
143
189
 
144
190
  if not path.exists():
145
- logger.debug("ACK for unknown envelope %s — ignoring", original_id[:8])
191
+ # No pending entry: a forged ACK for a message we never sent, or a
192
+ # replay after the entry was purged. Either way, ignore.
193
+ logger.debug("ACK for unknown envelope %s: ignoring", original_id[:8])
146
194
  return None
147
195
 
148
196
  try:
@@ -151,27 +199,46 @@ class AckTracker:
151
199
  logger.warning("Failed to read pending ACK %s: %s", original_id[:8], exc)
152
200
  return None
153
201
 
154
- # Replay/dedupe protection: only a still-pending entry may be confirmed.
155
- # A replayed ACK (or an ACK for an already-resolved envelope) is rejected
156
- # so it cannot re-confirm or mutate the entry.
157
- if pending.status != AckStatus.PENDING:
202
+ # Sender-identity binding (anti-forgery): the ACK must come from the
203
+ # identity the original message was delivered to. Reject any ACK whose
204
+ # sender is not the intended recipient (forgery by a third party).
205
+ # Comparison is fingerprint-normalization aware and fails closed on
206
+ # empty identities.
207
+ if not self._identity_matches(ack_envelope.sender, pending.recipient):
158
208
  logger.warning(
159
- "Duplicate/replayed ACK for %s (status=%s) — ignoring",
209
+ "Rejecting forged ACK for %s: sender %r is not tracked recipient %r",
160
210
  original_id[:8],
161
- pending.status.value,
211
+ ack_envelope.sender,
212
+ pending.recipient,
162
213
  )
163
214
  return None
164
215
 
165
- # Binding: the ACK must come from the identity the original message was
166
- # delivered to. The transport already verified the envelope signature, so
167
- # ack_envelope.sender is an authenticated identity — reject any ACK whose
168
- # sender is not the intended recipient (forgery by a third party).
169
- if ack_envelope.sender != pending.recipient:
216
+ # Optional cryptographic authentication of the ACK sender. Fail closed:
217
+ # a verifier error or a falsy result rejects the ACK.
218
+ if self._sender_verifier is not None:
219
+ try:
220
+ authenticated = bool(self._sender_verifier(ack_envelope))
221
+ except Exception as exc:
222
+ logger.warning(
223
+ "ACK sender verification errored for %s: %s", original_id[:8], exc
224
+ )
225
+ authenticated = False
226
+ if not authenticated:
227
+ logger.warning(
228
+ "Rejecting unauthenticated ACK for %s from %s (signature check failed)",
229
+ original_id[:8],
230
+ ack_envelope.sender,
231
+ )
232
+ return None
233
+
234
+ # Replay/dedupe protection: only a still-pending entry may be confirmed.
235
+ # A replayed ACK (or an ACK for an already-resolved or timed-out
236
+ # envelope) is rejected so it cannot re-confirm or mutate the entry.
237
+ if pending.status != AckStatus.PENDING:
170
238
  logger.warning(
171
- "ACK for %s from unauthorized sender %r (expected recipient %r) — rejecting",
239
+ "Duplicate/replayed ACK for %s (status=%s): ignoring",
172
240
  original_id[:8],
173
- ack_envelope.sender,
174
- pending.recipient,
241
+ pending.status.value,
175
242
  )
176
243
  return None
177
244