skcomms 0.2.8__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.
- {skcomms-0.2.8/src/skcomms.egg-info → skcomms-0.2.9}/PKG-INFO +1 -1
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/pairing_mirror.py +46 -1
- {skcomms-0.2.8 → skcomms-0.2.9/src/skcomms.egg-info}/PKG-INFO +1 -1
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/conftest.py +11 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_pairing_mirror.py +51 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/.github/workflows/ci.yml +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/.github/workflows/publish.yml +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/.github/workflows/pytest.yml +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/.github/workflows/qa.yml +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/.github/workflows/secret-scan.yml +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/.gitignore +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/.gitleaks.toml +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/CHANGELOG.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/CODE_OF_CONDUCT.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/CONTRIBUTING.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/DEPLOY-NOTES.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/Dockerfile +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/LICENSE +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/MISSION.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/README.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/SECURITY.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/SOP.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/constraints.txt +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/contrib/systemd/skcomms-api.service +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/contrib/systemd/skcomms-housekeep.service +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/contrib/systemd/skcomms-housekeep.timer +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/ARCHITECTURE.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/DISCORD_SETUP.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/HANDOFF-skfed-s2s-opus-delivery.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/PAIRING.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/REVOCATION_LATENCY.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/SYNCTHING_TOPOLOGY.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/access-plane-p7.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/access-plane-p8.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/cot-bridge.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/crypto-architecture.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/deploy-plan/skcomms-bulletproof-deploy.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/federation-data-comms-architecture.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/funnel-privacy.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/mode-b-tailnet-deploy.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/node_registry.example.yml +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/rfcs/RFC-0001-pq-ratchet-metadata-dual-identity.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/skfed-consent-design.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/skfed-federation-guide.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/state-sync-rearch-plan.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/superpowers/plans/2026-06-10-qr-pairing.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/superpowers/plans/2026-06-13-ble-mesh-p1-core.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/superpowers/plans/2026-06-13-lora-l1-core.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/superpowers/plans/2026-06-13-skglossa-g1-core.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/superpowers/plans/2026-06-13-skglossa-g2-macro-lexicon.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/superpowers/plans/2026-06-13-skglossa-g4-emergent.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/superpowers/plans/2026-06-13-tier3-adapter-runtime.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/superpowers/specs/2026-06-13-ble-mesh-proximity-transport-design.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/superpowers/specs/2026-06-13-lora-meshtastic-transport-design.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/superpowers/specs/2026-06-13-skglossa-design.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/docs/tak-vs-skfed-analysis.md +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/pyproject.toml +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/scripts/backfill_file_locations.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/scripts/bootstrap.sh +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/scripts/discord_smoke.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/scripts/drain_orphan_outbox.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/scripts/mode-b-setup.sh +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/scripts/refresh-constraints.sh +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/scripts/skfed-dns.sh +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/scripts/skfed-neutral-addresses.sh +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/scripts/skfed-sync-rearch.sh +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/scripts/telegram_smoke.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/scripts/templates/consent-mode.conf.tmpl +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/scripts/templates/realms.yml.tmpl +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/scripts/templates/tailscale-serve.sh.tmpl +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/scripts/test_skfed_sync_rearch.sh +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/setup.cfg +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/__init__.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/access/__init__.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/access/audit.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/access/config.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/access/exec.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/access/files.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/access/grants.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/access/knowledge.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/access/registry.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/access/routing.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/access/server.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/access/wiring.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/ack.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/adapters/__init__.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/adapters/base.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/adapters/discord.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/adapters/factory.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/adapters/fake.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/adapters/matrix.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/adapters/models.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/adapters/registry.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/adapters/slack.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/adapters/telegram.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/anon_queue.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/anon_transport.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/api.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/capabilities.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/capauth_validator.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/cli.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/cluster.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/compression.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/config.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/consent.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/consent_banfeeds.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/consent_captcha.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/consent_greylist.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/consent_group_gate.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/consent_groups.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/consent_moderation.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/consent_pipeline.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/consent_policy.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/consent_requests.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/consent_runtime.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/consent_tiering.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/consent_tokens.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/core.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/crypto.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/crypto_suites.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/did_router.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/discovery.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/envelope.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/federation.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/geo_seed.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/geo_store.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/glossa/__init__.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/glossa/codebook.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/glossa/codec.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/glossa/emergent.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/glossa/gloss.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/glossa/handshake.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/glossa/macros.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/glossa/message.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/glossa/session.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/grants.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/heartbeat.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/home.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/household_router.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/housekeeping.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/identity.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/integration.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/key_exchange.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/mailbox.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/marketplace.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/mcp_server.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/metrics.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/models.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/node_registry.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/nostr_discovery.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/observability.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/operator_probe.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/outbox.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/outbox_migrate.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/padding.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/pairing.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/paths.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/peers.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/pq_provider.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/pqdm.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/pqkem.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/pqkem_backend.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/pqroute.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/pqroute_transport.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/pqsig.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/profile_router.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/public_pairing.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/pubsub.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/pubsub_transport.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/queue.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/ratelimit.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/realm.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/registry.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/router.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/signaling.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/signing.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/skfed_announce.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/skfed_directory.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/skfed_readdr.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/skfed_resolve.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/skworld_manifest.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/souls_router.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/ssrf.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/store_forward.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/syncthing_rail.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/tofu.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transport.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/__init__.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/audio_track.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/ble/__init__.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/ble/gatt.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/ble/identity.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/ble/node.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/ble/noise.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/ble/protocol.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/ble/radio.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/ble/relay.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/broker_server.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/file.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/http_s2s.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/lora/__init__.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/lora/addressing.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/lora/framing.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/lora/interface.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/lora/meshtastic_iface.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/lora/store.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/lora/transport.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/nostr.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/p2p_connector.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/p2p_manager.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/p2p_session.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/signaling_base.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/signaling_broker.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/signaling_mailbox.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/syncthing.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/tailscale.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/video_track.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/webrtc.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/webrtc_media.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/transports/websocket.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms/trustbackup.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms.egg-info/SOURCES.txt +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms.egg-info/dependency_links.txt +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms.egg-info/entry_points.txt +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms.egg-info/requires.txt +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/src/skcomms.egg-info/top_level.txt +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/__init__.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/fixtures/pqdm2_from_dart.json +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/gen_pqdm2_fixture.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_access_exec.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_access_files.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_access_nonce_durable.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_access_rbac.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_access_routing.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_access_server.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_access_wiring.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_ack_bind.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_adapter_factory.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_adapter_fake.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_anon_queue.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_anon_transport.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_api_access_token.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_api_consent.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_api_consent_token_gate.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_api_cors.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_api_federation_inbox.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_api_geo_seed.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_api_geo_skcot_bridge.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_api_geo_units.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_api_health.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_api_send_signed_wire.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_api_skfed_directory.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_backfill_file_locations.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_ble_fragment.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_ble_gatt.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_ble_identity.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_ble_mesh.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_ble_noise.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_ble_pairing_bundle.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_ble_protocol.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_ble_radio.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_ble_relay.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_broker_server.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_capabilities.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_capauth_failclosed.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_capauth_key_reconcile.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_capauth_resolution.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_capauth_revoked_expired.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_channel_adapter.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_cluster_schema.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_config.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_consent.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_consent_ban_gate_fail_closed.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_consent_banfeeds.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_consent_captcha.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_consent_cli.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_consent_cli_mgmt.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_consent_greylist.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_consent_group_gate.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_consent_groups.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_consent_moderation.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_consent_pipeline.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_consent_policy.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_consent_requests.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_consent_runtime.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_consent_tiering.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_consent_tokens.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_core_inbound_beacon_skip.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_core_send_federated.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_core_send_signed.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_core_stop.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_crypto_failclosed.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_crypto_suites_q0.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_daemon_adapter_lifecycle.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_deadletter_ops.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_delivery_semantics.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_discord_adapter.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_encrypt_fail_closed.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_envelope_consent_token.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_envelope_v1.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_federation.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_federation_integration.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_geo_roundtrip_skcot.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_glossa_codebook.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_glossa_codec_l0.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_glossa_codec_l1.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_glossa_codec_l2.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_glossa_emergent.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_glossa_emergent_negotiator.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_glossa_emergent_protocol.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_glossa_gloss.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_glossa_handshake.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_glossa_handshake_lexicon.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_glossa_macros.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_glossa_macros_render.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_glossa_message.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_glossa_session.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_glossa_session_macros.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_glossa_to_human.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_grants.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_home_scaffold.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_housekeeping.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_http_s2s_star_recipient.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_http_s2s_transport.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_hybrid_envelope_sig.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_identity_env_override.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_identity_key_binding.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_identity_realm.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_identity_trust_backup.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_integration.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_key_exchange_transport_scoping.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_knowledge.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_lora_addressing.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_lora_framing.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_lora_interface.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_lora_meshtastic_iface.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_lora_store.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_lora_transport.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_mailbox.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_mailbox_encrypt_at_rest.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_mailbox_recipient_key.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_matrix_adapter.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_mode_b_setup.sh +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_no_subapp_dependency.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_node_registry.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_nonce_durable.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_nostr_discovery.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_observability.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_operator_cli.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_outbox_home_alignment.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_outbox_migrate.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_p2p_connector.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_p2p_manager.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_p2p_media.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_p2p_session.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_padding.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_pairing.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_pairing_cli.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_paths_scoping.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_peer_inbox_traversal.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_peers.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_pqc_cutover.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_pqc_shim.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_pqdm.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_pqdm2.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_pqdm2_interop.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_pqkem.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_pqroute.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_pqroute_padding.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_pqroute_transport.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_pqsig.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_prune_inbox.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_public_pairing.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_queue_bounds.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_receiver_wire_ttl_expiry.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_registry.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_registry_cli.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_router_failure_log_dedup.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_router_perm_backoff.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_router_perm_backoff_fixes.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_router_quarantine.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_router_rail_ordering.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_router_route_anon.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_router_route_signed.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_router_star_broadcast_routing.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_router_unsigned_excludes_https_s2s.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_sign_fail_closed.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_signaling_broker.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_signaling_mailbox.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_signaling_select.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_skfed_announce.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_skfed_cli.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_skfed_directory.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_skfed_dns.sh +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_skfed_operator_pin.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_skfed_readdr.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_skfed_resolve.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_skfed_ssrf.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_skworld_manifest.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_slack_adapter.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_smoke.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_ssrf_guard.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_store_forward.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_syncthing_rail.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_syncthing_recipient_guard.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_telegram_adapter.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_tofu.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_typed_message_content.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_webrtc_bridge_lifecycle.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_webrtc_ice_logging.py +0 -0
- {skcomms-0.2.8 → skcomms-0.2.9}/tests/test_webrtc_signaling_log_dedup.py +0 -0
|
@@ -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
|
|
|
@@ -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
|
|
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
|
|
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
|
{skcomms-0.2.8 → skcomms-0.2.9}/docs/superpowers/plans/2026-06-13-skglossa-g2-macro-lexicon.md
RENAMED
|
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
|
|
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
|
|
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
|
|
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
|