capauth 0.3.2__tar.gz → 0.3.3__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 (369) hide show
  1. {capauth-0.3.2 → capauth-0.3.3}/PKG-INFO +1 -1
  2. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/pairing/proof.py +36 -6
  3. {capauth-0.3.2 → capauth-0.3.3}/src/capauth.egg-info/PKG-INFO +1 -1
  4. {capauth-0.3.2 → capauth-0.3.3}/tests/test_enrollment_proof_helper.py +86 -4
  5. {capauth-0.3.2 → capauth-0.3.3}/.cursorrules +0 -0
  6. {capauth-0.3.2 → capauth-0.3.3}/.dockerignore +0 -0
  7. {capauth-0.3.2 → capauth-0.3.3}/.github/workflows/ci.yml +0 -0
  8. {capauth-0.3.2 → capauth-0.3.3}/.github/workflows/docs-check.yml +0 -0
  9. {capauth-0.3.2 → capauth-0.3.3}/.github/workflows/nextcloud-appstore.yml +0 -0
  10. {capauth-0.3.2 → capauth-0.3.3}/.github/workflows/publish.yml +0 -0
  11. {capauth-0.3.2 → capauth-0.3.3}/.github/workflows/pytest.yml +0 -0
  12. {capauth-0.3.2 → capauth-0.3.3}/.github/workflows/secret-scan.yml +0 -0
  13. {capauth-0.3.2 → capauth-0.3.3}/.gitignore +0 -0
  14. {capauth-0.3.2 → capauth-0.3.3}/.gitleaks-baseline.json +0 -0
  15. {capauth-0.3.2 → capauth-0.3.3}/AGENTS.md +0 -0
  16. {capauth-0.3.2 → capauth-0.3.3}/AI-ADVOCATE.md +0 -0
  17. {capauth-0.3.2 → capauth-0.3.3}/ARCHITECTURE.md +0 -0
  18. {capauth-0.3.2 → capauth-0.3.3}/CHANGELOG.md +0 -0
  19. {capauth-0.3.2 → capauth-0.3.3}/CLAUDE.md +0 -0
  20. {capauth-0.3.2 → capauth-0.3.3}/CODE_OF_CONDUCT.md +0 -0
  21. {capauth-0.3.2 → capauth-0.3.3}/CONTRIBUTING.md +0 -0
  22. {capauth-0.3.2 → capauth-0.3.3}/Dockerfile +0 -0
  23. {capauth-0.3.2 → capauth-0.3.3}/Dockerfile.authentik-capauth +0 -0
  24. {capauth-0.3.2 → capauth-0.3.3}/LICENSE +0 -0
  25. {capauth-0.3.2 → capauth-0.3.3}/MANIFEST.in +0 -0
  26. {capauth-0.3.2 → capauth-0.3.3}/MISSION.md +0 -0
  27. {capauth-0.3.2 → capauth-0.3.3}/README.md +0 -0
  28. {capauth-0.3.2 → capauth-0.3.3}/SECURITY.md +0 -0
  29. {capauth-0.3.2 → capauth-0.3.3}/SKILL.md +0 -0
  30. {capauth-0.3.2 → capauth-0.3.3}/SOP.md +0 -0
  31. {capauth-0.3.2 → capauth-0.3.3}/authentik-custom/capauth_migrate.py +0 -0
  32. {capauth-0.3.2 → capauth-0.3.3}/authentik-custom/imghdr.py +0 -0
  33. {capauth-0.3.2 → capauth-0.3.3}/authentik-custom/user_settings.py +0 -0
  34. {capauth-0.3.2 → capauth-0.3.3}/bin/cli.js +0 -0
  35. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/.gitignore +0 -0
  36. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/README.md +0 -0
  37. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/RELEASING.md +0 -0
  38. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/background.js +0 -0
  39. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/content_scripts/autofill.js +0 -0
  40. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/content_scripts/cross_origin_signing.js +0 -0
  41. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/content_scripts/detector.js +0 -0
  42. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/content_scripts/provider.js +0 -0
  43. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/content_scripts/provider_bridge.js +0 -0
  44. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/icons/icon.svg +0 -0
  45. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/icons/icon128.png +0 -0
  46. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/icons/icon16.png +0 -0
  47. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/icons/icon48.png +0 -0
  48. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/lib/bunker-e2e.js +0 -0
  49. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/lib/keyqr.js +0 -0
  50. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/lib/keyvault.js +0 -0
  51. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/lib/openpgp.js +0 -0
  52. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/lib/signer-backends.js +0 -0
  53. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/manifest.firefox.json +0 -0
  54. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/manifest.json +0 -0
  55. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/native-host/capauth_signer.py +0 -0
  56. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/native-host/com.capauth.signer.json +0 -0
  57. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/native-host/install.sh +0 -0
  58. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/options/options.html +0 -0
  59. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/options/options.js +0 -0
  60. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/package-lock.json +0 -0
  61. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/package.json +0 -0
  62. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/popup/popup.css +0 -0
  63. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/popup/popup.html +0 -0
  64. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/popup/popup.js +0 -0
  65. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/scripts/build.js +0 -0
  66. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/scripts/generate-icons.js +0 -0
  67. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/stages/AuthenticatorSelectionStage.js +0 -0
  68. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/stages/CapAuthStage.js +0 -0
  69. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/stages/QRDisplayStage.js +0 -0
  70. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/stages/SignatureVerificationStage.js +0 -0
  71. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/stages/StagePipeline.js +0 -0
  72. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/submission/STORE_LISTING.md +0 -0
  73. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/submission/privacy-policy.md +0 -0
  74. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/tests/fixtures/test_key.json +0 -0
  75. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/tests/unit/background.test.js +0 -0
  76. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/tests/unit/bunker_e2e.test.js +0 -0
  77. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/tests/unit/detector.test.js +0 -0
  78. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/tests/unit/keyqr.test.js +0 -0
  79. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/tests/unit/keyvault.test.js +0 -0
  80. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/tests/unit/openpgp.test.js +0 -0
  81. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/tests/unit/provider_api.test.js +0 -0
  82. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/tests/unit/provider_tierb.test.js +0 -0
  83. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/tests/unit/remote_bunker.test.js +0 -0
  84. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/tests/unit/signer_backends.test.js +0 -0
  85. {capauth-0.3.2 → capauth-0.3.3}/browser-extension/vitest.config.js +0 -0
  86. {capauth-0.3.2 → capauth-0.3.3}/deploy/capauth-service/.env.example +0 -0
  87. {capauth-0.3.2 → capauth-0.3.3}/deploy/capauth-service/deploy.sh +0 -0
  88. {capauth-0.3.2 → capauth-0.3.3}/deploy/capauth-service/docker-compose.yml +0 -0
  89. {capauth-0.3.2 → capauth-0.3.3}/deploy/capauth-service/systemd/capauth-backup.service +0 -0
  90. {capauth-0.3.2 → capauth-0.3.3}/deploy/capauth-service/systemd/capauth-backup.timer +0 -0
  91. {capauth-0.3.2 → capauth-0.3.3}/deploy/forgejo-capauth/.env.example +0 -0
  92. {capauth-0.3.2 → capauth-0.3.3}/deploy/forgejo-capauth/docker-compose.yml +0 -0
  93. {capauth-0.3.2 → capauth-0.3.3}/deploy/forgejo-capauth/preflight.sh +0 -0
  94. {capauth-0.3.2 → capauth-0.3.3}/docs/AIRGAPPED_SIGNING_WORKSTATION.md +0 -0
  95. {capauth-0.3.2 → capauth-0.3.3}/docs/ARCHITECTURE.md +0 -0
  96. {capauth-0.3.2 → capauth-0.3.3}/docs/AUTHENTIK_CUSTOM_STAGE.md +0 -0
  97. {capauth-0.3.2 → capauth-0.3.3}/docs/AUTHENTIK_DEPLOYMENT_SKSSO.md +0 -0
  98. {capauth-0.3.2 → capauth-0.3.3}/docs/AUTHENTIK_FORGEJO_DEPLOYMENT.md +0 -0
  99. {capauth-0.3.2 → capauth-0.3.3}/docs/AUTHENTIK_STRATEGY_DECISION.md +0 -0
  100. {capauth-0.3.2 → capauth-0.3.3}/docs/CAPAUTH_BUNKER_REMOTE_SIGNER.md +0 -0
  101. {capauth-0.3.2 → capauth-0.3.3}/docs/CAPAUTH_OIDC_IDP.md +0 -0
  102. {capauth-0.3.2 → capauth-0.3.3}/docs/CLAIMS.md +0 -0
  103. {capauth-0.3.2 → capauth-0.3.3}/docs/COLD_MACHINE_BOOTSTRAP_AND_DR.md +0 -0
  104. {capauth-0.3.2 → capauth-0.3.3}/docs/CRYPTO_SPEC.md +0 -0
  105. {capauth-0.3.2 → capauth-0.3.3}/docs/ENTERPRISE_MANAGED_KEYS.md +0 -0
  106. {capauth-0.3.2 → capauth-0.3.3}/docs/INTEGRATION_BLUEPRINT.md +0 -0
  107. {capauth-0.3.2 → capauth-0.3.3}/docs/M0-capauth-api-v1.md +0 -0
  108. {capauth-0.3.2 → capauth-0.3.3}/docs/M0-capauth-importer-inventory.md +0 -0
  109. {capauth-0.3.2 → capauth-0.3.3}/docs/PQC_CAPAUTH_REVISIT.md +0 -0
  110. {capauth-0.3.2 → capauth-0.3.3}/docs/PQC_ROOT_MIGRATION.md +0 -0
  111. {capauth-0.3.2 → capauth-0.3.3}/docs/PROTOCOL.md +0 -0
  112. {capauth-0.3.2 → capauth-0.3.3}/docs/ROOT_ROTATION_CEREMONY.md +0 -0
  113. {capauth-0.3.2 → capauth-0.3.3}/docs/TRUST_GRAPH_COORD_PROJECTION.md +0 -0
  114. {capauth-0.3.2 → capauth-0.3.3}/docs/ZERO_KNOWLEDGE_PROFILE.md +0 -0
  115. {capauth-0.3.2 → capauth-0.3.3}/docs/authentik-capauth.md +0 -0
  116. {capauth-0.3.2 → capauth-0.3.3}/docs/deploy-plan/capauth-bulletproof-deploy.md +0 -0
  117. {capauth-0.3.2 → capauth-0.3.3}/index.d.ts +0 -0
  118. {capauth-0.3.2 → capauth-0.3.3}/index.js +0 -0
  119. {capauth-0.3.2 → capauth-0.3.3}/openclaw-plugin.archived-2026-04-23/package.json +0 -0
  120. {capauth-0.3.2 → capauth-0.3.3}/openclaw-plugin.archived-2026-04-23/src/index.ts +0 -0
  121. {capauth-0.3.2 → capauth-0.3.3}/openclaw-plugin.archived-2026-04-23/src/openclaw.plugin.json +0 -0
  122. {capauth-0.3.2 → capauth-0.3.3}/package.json +0 -0
  123. {capauth-0.3.2 → capauth-0.3.3}/phone-signer/app.js +0 -0
  124. {capauth-0.3.2 → capauth-0.3.3}/phone-signer/icons/icon-192.png +0 -0
  125. {capauth-0.3.2 → capauth-0.3.3}/phone-signer/icons/icon-512.png +0 -0
  126. {capauth-0.3.2 → capauth-0.3.3}/phone-signer/index.html +0 -0
  127. {capauth-0.3.2 → capauth-0.3.3}/phone-signer/lib/bunker-e2e.js +0 -0
  128. {capauth-0.3.2 → capauth-0.3.3}/phone-signer/lib/bunker-signer.js +0 -0
  129. {capauth-0.3.2 → capauth-0.3.3}/phone-signer/lib/canonical.js +0 -0
  130. {capauth-0.3.2 → capauth-0.3.3}/phone-signer/lib/keyqr.js +0 -0
  131. {capauth-0.3.2 → capauth-0.3.3}/phone-signer/lib/keyvault.js +0 -0
  132. {capauth-0.3.2 → capauth-0.3.3}/phone-signer/manifest.webmanifest +0 -0
  133. {capauth-0.3.2 → capauth-0.3.3}/phone-signer/sw.js +0 -0
  134. {capauth-0.3.2 → capauth-0.3.3}/phone-signer/vendor/openpgp.min.js +0 -0
  135. {capauth-0.3.2 → capauth-0.3.3}/phone-signer/vendor/qrcode-generator.js +0 -0
  136. {capauth-0.3.2 → capauth-0.3.3}/pyproject.toml +0 -0
  137. {capauth-0.3.2 → capauth-0.3.3}/ruff-0.15.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl +0 -0
  138. {capauth-0.3.2 → capauth-0.3.3}/scripts/build_authentik_capauth_image.sh +0 -0
  139. {capauth-0.3.2 → capauth-0.3.3}/scripts/capauth-backup.sh +0 -0
  140. {capauth-0.3.2 → capauth-0.3.3}/scripts/capauth-restore.sh +0 -0
  141. {capauth-0.3.2 → capauth-0.3.3}/scripts/migrate_canonical_subjects.py +0 -0
  142. {capauth-0.3.2 → capauth-0.3.3}/scripts/pqc_ceremony_dryrun.py +0 -0
  143. {capauth-0.3.2 → capauth-0.3.3}/scripts/provision_agent_profiles.py +0 -0
  144. {capauth-0.3.2 → capauth-0.3.3}/setup.cfg +0 -0
  145. {capauth-0.3.2 → capauth-0.3.3}/skill.yaml +0 -0
  146. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/__init__.py +0 -0
  147. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/agent_identity.py +0 -0
  148. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/apps.py +0 -0
  149. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/authentik/__init__.py +0 -0
  150. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/authentik/api.py +0 -0
  151. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/authentik/blueprints/capauth-pgp-login.yaml +0 -0
  152. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/authentik/claims_mapper.py +0 -0
  153. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/authentik/nonce_store.py +0 -0
  154. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/authentik/stage.py +0 -0
  155. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/authentik/urls.py +0 -0
  156. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/authentik/verifier.py +0 -0
  157. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/authz.py +0 -0
  158. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/cli.py +0 -0
  159. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/crypto/__init__.py +0 -0
  160. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/crypto/base.py +0 -0
  161. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/crypto/gnupg_backend.py +0 -0
  162. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/crypto/pgpy_backend.py +0 -0
  163. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/crypto/sequoia_backend.py +0 -0
  164. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/crypto/skpgp_backend.py +0 -0
  165. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/custody.py +0 -0
  166. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/did.py +0 -0
  167. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/discovery/__init__.py +0 -0
  168. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/discovery/base.py +0 -0
  169. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/discovery/file_discovery.py +0 -0
  170. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/discovery/mdns.py +0 -0
  171. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/discovery/syncthing.py +0 -0
  172. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/exceptions.py +0 -0
  173. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/identity.py +0 -0
  174. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/identity_class.py +0 -0
  175. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integration.py +0 -0
  176. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/__init__.py +0 -0
  177. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/forgejo/__init__.py +0 -0
  178. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/forgejo/auth_flow.py +0 -0
  179. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/forgejo/cli.py +0 -0
  180. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/forgejo/config.py +0 -0
  181. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/forgejo/forgejo_api.py +0 -0
  182. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/forgejo/oidc_provider.py +0 -0
  183. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/forgejo/tests/__init__.py +0 -0
  184. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/forgejo/tests/test_auth_flow.py +0 -0
  185. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/forgejo/tests/test_cli.py +0 -0
  186. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/forgejo/tests/test_config.py +0 -0
  187. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/forgejo/tests/test_forgejo_api.py +0 -0
  188. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/forgejo/tests/test_oidc_provider.py +0 -0
  189. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/.gitignore +0 -0
  190. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/.nextcloudignore +0 -0
  191. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/CSR-PR.md +0 -0
  192. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/INFO-XML-CHANGES.md +0 -0
  193. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/PUBLISHING.md +0 -0
  194. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/README.md +0 -0
  195. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/appinfo/info.xml +0 -0
  196. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/appinfo/routes.php +0 -0
  197. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/build-release.sh +0 -0
  198. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/certificates/.gitignore +0 -0
  199. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/certificates/capauth.crt +0 -0
  200. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/certificates/capauth.csr +0 -0
  201. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/composer.json +0 -0
  202. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/css/login.css +0 -0
  203. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/js/login.js +0 -0
  204. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/krankerl.toml +0 -0
  205. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/lib/AlternativeLogin/CapAuthLogin.php +0 -0
  206. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/lib/AppInfo/Application.php +0 -0
  207. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/lib/BackgroundJob/PruneExpiredNonces.php +0 -0
  208. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/lib/Controller/LoginController.php +0 -0
  209. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/lib/Controller/TokenController.php +0 -0
  210. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/lib/Db/KeyRegistry.php +0 -0
  211. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/lib/Exception/CapAuthUnauthorizedException.php +0 -0
  212. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/lib/Middleware/PgpVerificationMiddleware.php +0 -0
  213. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/lib/Migration/Version000300Date20260301000000.php +0 -0
  214. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/lib/Provider/CapAuthProvider.php +0 -0
  215. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/lib/Service/ChallengeService.php +0 -0
  216. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/lib/Service/GroupSyncService.php +0 -0
  217. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/lib/Service/UserProvisioningService.php +0 -0
  218. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/lib/Service/VerifierService.php +0 -0
  219. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/lib/User/Backend.php +0 -0
  220. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/phpunit.xml +0 -0
  221. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/screenshots/README.md +0 -0
  222. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/screenshots/login.png +0 -0
  223. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/templates/login.php +0 -0
  224. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/tests/bootstrap.php +0 -0
  225. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/tests/integration/FullAuthFlowTest.php +0 -0
  226. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/tests/oidc/oidc_e2e_login_test.py +0 -0
  227. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/tests/unit/Controller/LoginControllerTest.php +0 -0
  228. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/tests/unit/Db/KeyRegistryTest.php +0 -0
  229. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/tests/unit/Provider/CapAuthProviderTest.php +0 -0
  230. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/tests/unit/Service/ChallengeServiceTest.php +0 -0
  231. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/tests/unit/Service/VerifierServiceTest.php +0 -0
  232. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/integrations/nextcloud/tests/unit/User/BackendTest.php +0 -0
  233. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/key_transparency.py +0 -0
  234. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/login.py +0 -0
  235. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/manifest.py +0 -0
  236. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/mesh.py +0 -0
  237. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/migrations/0001_initial.py +0 -0
  238. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/migrations/0002_widen_fingerprint_to_64.py +0 -0
  239. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/migrations/__init__.py +0 -0
  240. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/models.py +0 -0
  241. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/pairing/__init__.py +0 -0
  242. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/pairing/canonicalize.py +0 -0
  243. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/pairing/kernel.py +0 -0
  244. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/pairing/operator_session.py +0 -0
  245. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/pairing/records.py +0 -0
  246. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/pairing/store.py +0 -0
  247. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/pairing/window.py +0 -0
  248. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/pma.py +0 -0
  249. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/pqc_confidentiality.py +0 -0
  250. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/pqc_identity.py +0 -0
  251. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/pqc_root_identity.py +0 -0
  252. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/pqc_tokens.py +0 -0
  253. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/profile.py +0 -0
  254. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/provisioning.py +0 -0
  255. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/registry.py +0 -0
  256. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/seal.py +0 -0
  257. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/service/__init__.py +0 -0
  258. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/service/app.py +0 -0
  259. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/service/bunker.py +0 -0
  260. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/service/bunker_e2e.py +0 -0
  261. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/service/keystore.py +0 -0
  262. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/service/oidc/__init__.py +0 -0
  263. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/service/oidc/clients.example.json +0 -0
  264. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/service/oidc/clients.py +0 -0
  265. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/service/oidc/passkey.py +0 -0
  266. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/service/oidc/provider.py +0 -0
  267. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/service/oidc/signing_key.py +0 -0
  268. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/service/oidc/store.py +0 -0
  269. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/service/push.py +0 -0
  270. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/service/server.py +0 -0
  271. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/subject.py +0 -0
  272. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/sync.py +0 -0
  273. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/testing.py +0 -0
  274. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/tokens.py +0 -0
  275. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/trust/__init__.py +0 -0
  276. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/trust/calibration.py +0 -0
  277. {capauth-0.3.2 → capauth-0.3.3}/src/capauth/trust/graph.py +0 -0
  278. {capauth-0.3.2 → capauth-0.3.3}/src/capauth.egg-info/SOURCES.txt +0 -0
  279. {capauth-0.3.2 → capauth-0.3.3}/src/capauth.egg-info/dependency_links.txt +0 -0
  280. {capauth-0.3.2 → capauth-0.3.3}/src/capauth.egg-info/entry_points.txt +0 -0
  281. {capauth-0.3.2 → capauth-0.3.3}/src/capauth.egg-info/requires.txt +0 -0
  282. {capauth-0.3.2 → capauth-0.3.3}/src/capauth.egg-info/top_level.txt +0 -0
  283. {capauth-0.3.2 → capauth-0.3.3}/stages/ak-stage-capauth/.gitignore +0 -0
  284. {capauth-0.3.2 → capauth-0.3.3}/stages/ak-stage-capauth/package-lock.json +0 -0
  285. {capauth-0.3.2 → capauth-0.3.3}/stages/ak-stage-capauth/package.json +0 -0
  286. {capauth-0.3.2 → capauth-0.3.3}/stages/ak-stage-capauth/src/CapAuthStage.test.js +0 -0
  287. {capauth-0.3.2 → capauth-0.3.3}/stages/ak-stage-capauth/src/CapAuthStage.vue +0 -0
  288. {capauth-0.3.2 → capauth-0.3.3}/stages/ak-stage-capauth/src/index.js +0 -0
  289. {capauth-0.3.2 → capauth-0.3.3}/stages/ak-stage-capauth/vite.config.js +0 -0
  290. {capauth-0.3.2 → capauth-0.3.3}/tests/__init__.py +0 -0
  291. {capauth-0.3.2 → capauth-0.3.3}/tests/conftest.py +0 -0
  292. {capauth-0.3.2 → capauth-0.3.3}/tests/fixtures/bunker_e2e_v1_vector.json +0 -0
  293. {capauth-0.3.2 → capauth-0.3.3}/tests/fixtures/canonical_nonce_v2_vector.json +0 -0
  294. {capauth-0.3.2 → capauth-0.3.3}/tests/test_agent_identity.py +0 -0
  295. {capauth-0.3.2 → capauth-0.3.3}/tests/test_agentrun_capabilities.py +0 -0
  296. {capauth-0.3.2 → capauth-0.3.3}/tests/test_audience_mint_helper.py +0 -0
  297. {capauth-0.3.2 → capauth-0.3.3}/tests/test_audience_tokens.py +0 -0
  298. {capauth-0.3.2 → capauth-0.3.3}/tests/test_authz.py +0 -0
  299. {capauth-0.3.2 → capauth-0.3.3}/tests/test_authz_legacy_grace.py +0 -0
  300. {capauth-0.3.2 → capauth-0.3.3}/tests/test_authz_signature_gate.py +0 -0
  301. {capauth-0.3.2 → capauth-0.3.3}/tests/test_backup_restore.py +0 -0
  302. {capauth-0.3.2 → capauth-0.3.3}/tests/test_bunker.py +0 -0
  303. {capauth-0.3.2 → capauth-0.3.3}/tests/test_bunker_e2e.py +0 -0
  304. {capauth-0.3.2 → capauth-0.3.3}/tests/test_canonicalize.py +0 -0
  305. {capauth-0.3.2 → capauth-0.3.3}/tests/test_capauth_service_deploy.py +0 -0
  306. {capauth-0.3.2 → capauth-0.3.3}/tests/test_change_capabilities.py +0 -0
  307. {capauth-0.3.2 → capauth-0.3.3}/tests/test_claims_mapper.py +0 -0
  308. {capauth-0.3.2 → capauth-0.3.3}/tests/test_crypto.py +0 -0
  309. {capauth-0.3.2 → capauth-0.3.3}/tests/test_custody.py +0 -0
  310. {capauth-0.3.2 → capauth-0.3.3}/tests/test_device_key_proof.py +0 -0
  311. {capauth-0.3.2 → capauth-0.3.3}/tests/test_did_capability_relationships.py +0 -0
  312. {capauth-0.3.2 → capauth-0.3.3}/tests/test_fingerprint_v6.py +0 -0
  313. {capauth-0.3.2 → capauth-0.3.3}/tests/test_forgejo_preflight.py +0 -0
  314. {capauth-0.3.2 → capauth-0.3.3}/tests/test_home_resolution.py +0 -0
  315. {capauth-0.3.2 → capauth-0.3.3}/tests/test_identity.py +0 -0
  316. {capauth-0.3.2 → capauth-0.3.3}/tests/test_identity_class.py +0 -0
  317. {capauth-0.3.2 → capauth-0.3.3}/tests/test_identity_ttl.py +0 -0
  318. {capauth-0.3.2 → capauth-0.3.3}/tests/test_integration.py +0 -0
  319. {capauth-0.3.2 → capauth-0.3.3}/tests/test_key_revocation_expiry.py +0 -0
  320. {capauth-0.3.2 → capauth-0.3.3}/tests/test_key_transparency.py +0 -0
  321. {capauth-0.3.2 → capauth-0.3.3}/tests/test_login.py +0 -0
  322. {capauth-0.3.2 → capauth-0.3.3}/tests/test_manifest.py +0 -0
  323. {capauth-0.3.2 → capauth-0.3.3}/tests/test_mesh.py +0 -0
  324. {capauth-0.3.2 → capauth-0.3.3}/tests/test_models_pqc_q0.py +0 -0
  325. {capauth-0.3.2 → capauth-0.3.3}/tests/test_nextcloud_app.py +0 -0
  326. {capauth-0.3.2 → capauth-0.3.3}/tests/test_no_subapp_dependency.py +0 -0
  327. {capauth-0.3.2 → capauth-0.3.3}/tests/test_nonce_store.py +0 -0
  328. {capauth-0.3.2 → capauth-0.3.3}/tests/test_oidc_idp.py +0 -0
  329. {capauth-0.3.2 → capauth-0.3.3}/tests/test_oidc_idp_e2e.py +0 -0
  330. {capauth-0.3.2 → capauth-0.3.3}/tests/test_operator_session.py +0 -0
  331. {capauth-0.3.2 → capauth-0.3.3}/tests/test_pairing.py +0 -0
  332. {capauth-0.3.2 → capauth-0.3.3}/tests/test_pairing_base_dir_env.py +0 -0
  333. {capauth-0.3.2 → capauth-0.3.3}/tests/test_passkey.py +0 -0
  334. {capauth-0.3.2 → capauth-0.3.3}/tests/test_pma.py +0 -0
  335. {capauth-0.3.2 → capauth-0.3.3}/tests/test_pqc_confidentiality.py +0 -0
  336. {capauth-0.3.2 → capauth-0.3.3}/tests/test_pqc_identity.py +0 -0
  337. {capauth-0.3.2 → capauth-0.3.3}/tests/test_pqc_root_integration.py +0 -0
  338. {capauth-0.3.2 → capauth-0.3.3}/tests/test_pqc_t3_gate.py +0 -0
  339. {capauth-0.3.2 → capauth-0.3.3}/tests/test_pqc_tokens.py +0 -0
  340. {capauth-0.3.2 → capauth-0.3.3}/tests/test_profile.py +0 -0
  341. {capauth-0.3.2 → capauth-0.3.3}/tests/test_provisioning.py +0 -0
  342. {capauth-0.3.2 → capauth-0.3.3}/tests/test_push.py +0 -0
  343. {capauth-0.3.2 → capauth-0.3.3}/tests/test_py313_compat.py +0 -0
  344. {capauth-0.3.2 → capauth-0.3.3}/tests/test_qr_login.py +0 -0
  345. {capauth-0.3.2 → capauth-0.3.3}/tests/test_real_pgp_e2e.py +0 -0
  346. {capauth-0.3.2 → capauth-0.3.3}/tests/test_register_cli.py +0 -0
  347. {capauth-0.3.2 → capauth-0.3.3}/tests/test_registry.py +0 -0
  348. {capauth-0.3.2 → capauth-0.3.3}/tests/test_seal.py +0 -0
  349. {capauth-0.3.2 → capauth-0.3.3}/tests/test_sequoia_backend.py +0 -0
  350. {capauth-0.3.2 → capauth-0.3.3}/tests/test_service.py +0 -0
  351. {capauth-0.3.2 → capauth-0.3.3}/tests/test_service_authz_decide.py +0 -0
  352. {capauth-0.3.2 → capauth-0.3.3}/tests/test_service_v6_fingerprint.py +0 -0
  353. {capauth-0.3.2 → capauth-0.3.3}/tests/test_skpgp_backend.py +0 -0
  354. {capauth-0.3.2 → capauth-0.3.3}/tests/test_subject.py +0 -0
  355. {capauth-0.3.2 → capauth-0.3.3}/tests/test_syncthing_discovery.py +0 -0
  356. {capauth-0.3.2 → capauth-0.3.3}/tests/test_testing_helper.py +0 -0
  357. {capauth-0.3.2 → capauth-0.3.3}/tests/test_token_gc.py +0 -0
  358. {capauth-0.3.2 → capauth-0.3.3}/tests/test_token_store_flag.py +0 -0
  359. {capauth-0.3.2 → capauth-0.3.3}/tests/test_tokens.py +0 -0
  360. {capauth-0.3.2 → capauth-0.3.3}/tests/test_trust_calibration.py +0 -0
  361. {capauth-0.3.2 → capauth-0.3.3}/tests/test_trust_graph.py +0 -0
  362. {capauth-0.3.2 → capauth-0.3.3}/tests/test_trust_graph_coord_audit.py +0 -0
  363. {capauth-0.3.2 → capauth-0.3.3}/tests/test_verifier.py +0 -0
  364. {capauth-0.3.2 → capauth-0.3.3}/tests/test_version_honesty.py +0 -0
  365. {capauth-0.3.2 → capauth-0.3.3}/tests/test_zk_profile.py +0 -0
  366. {capauth-0.3.2 → capauth-0.3.3}/tools/build-sq.sh +0 -0
  367. {capauth-0.3.2 → capauth-0.3.3}/urllib.error +0 -0
  368. {capauth-0.3.2 → capauth-0.3.3}/web/README.md +0 -0
  369. {capauth-0.3.2 → capauth-0.3.3}/web/stages/capauth/CapAuthStage.ts +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: capauth
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: Capability-based Authentication — decentralized, PGP-based identity and authorization
5
5
  Author-email: smilinTux <admin@smilintux.org>
6
6
  License: GPL-3.0-or-later
@@ -173,6 +173,36 @@ def _is_pgp(key: Optional[str]) -> bool:
173
173
  return "BEGIN PGP" in (key or "")
174
174
 
175
175
 
176
+ def _require_subject(subject: Optional[str]) -> str:
177
+ """Reject an absent or blank subject, loudly, at the call.
178
+
179
+ ``subject`` is a REQUIRED keyword on every public builder here, but a caller
180
+ can still hand us ``None`` at runtime (an Optional threaded down from its own
181
+ config, a dict lookup that missed). Falling back to a derived default there
182
+ is the one thing this module must never do.
183
+
184
+ Why: the challenge binds the subject, so a proof built for subject A does not
185
+ verify for subject B. If the builder quietly substituted its own default
186
+ while ``enroll_device`` was called with the caller's real subject, the device
187
+ would sign honestly, capauth would reject the proof, and the enrollment would
188
+ land on the TOFU floor behind a success response with nothing raised. That is
189
+ exactly the silent downgrade card N10 exists to remove.
190
+
191
+ This bit for real: capauth's derived default is the full 40-char
192
+ ``fingerprint_for`` value, while skchat's own derivation is a 16-char device
193
+ id. Two individually reasonable defaults, different signed bytes, and the
194
+ mismatch is invisible until the security boundary rejects it. Caught in
195
+ review by clawd-43 on 2026-08-17 before it shipped to a caller.
196
+ """
197
+ if subject is None or not str(subject).strip():
198
+ raise ProofSigningError(
199
+ "subject is required: the enrollment challenge binds it, so it must be "
200
+ "the SAME subject you pass to enroll_device(). Deriving one here would "
201
+ "silently change the signed bytes and downgrade the enrollment to tofu."
202
+ )
203
+ return str(subject)
204
+
205
+
176
206
  def _resolve_subject(pubkey: str, subject: Optional[str]) -> tuple[str, str]:
177
207
  """Derive ``(fingerprint, canonical_subject)`` exactly as ``enroll_device`` will.
178
208
 
@@ -208,7 +238,7 @@ def _coerce_mode(mode: EnrollmentMode | str) -> EnrollmentMode:
208
238
  def enrollment_challenge(
209
239
  pubkey: str,
210
240
  *,
211
- subject: Optional[str] = None,
241
+ subject: str,
212
242
  mode: EnrollmentMode | str = EnrollmentMode.VERIFIED,
213
243
  ) -> bytes:
214
244
  """The exact bytes an enrollment's proof must sign, from the PUBLIC key alone.
@@ -245,7 +275,7 @@ def enrollment_challenge(
245
275
  PairingError: If ``mode`` is not a known enrollment mode.
246
276
  """
247
277
  resolved = _coerce_mode(mode)
248
- fingerprint, canonical = _resolve_subject(pubkey, subject)
278
+ fingerprint, canonical = _resolve_subject(pubkey, _require_subject(subject))
249
279
  if resolved is EnrollmentMode.ATTESTED:
250
280
  return attested_challenge(fingerprint, canonical)
251
281
  if resolved is EnrollmentMode.VERIFIED:
@@ -340,8 +370,8 @@ def build_verified_proof(
340
370
  pubkey: str,
341
371
  *,
342
372
  private_key: Any,
373
+ subject: str,
343
374
  passphrase: str = "",
344
- subject: Optional[str] = None,
345
375
  ) -> EnrollmentProof:
346
376
  """Sign the ``verified`` enrollment challenge with the device's OWN key.
347
377
 
@@ -379,7 +409,7 @@ def build_verified_proof(
379
409
  capauth.exceptions.SubjectNamingError: If ``subject`` does not conform
380
410
  to the canonical fqid grammar even after alias translation.
381
411
  """
382
- fingerprint, canonical = _resolve_subject(pubkey, subject)
412
+ fingerprint, canonical = _resolve_subject(pubkey, _require_subject(subject))
383
413
  challenge = verified_challenge(fingerprint, canonical)
384
414
  signature = _sign(challenge, private_key, passphrase, pgp=_is_pgp(pubkey))
385
415
  _self_check(pubkey, signature, challenge, what="verified proof")
@@ -398,8 +428,8 @@ def build_attested_proof(
398
428
  *,
399
429
  operator_pubkey: str,
400
430
  operator_private_key: Any,
431
+ subject: str,
401
432
  passphrase: str = "",
402
- subject: Optional[str] = None,
403
433
  ) -> EnrollmentProof:
404
434
  """Sign the ``attested`` challenge with a VOUCHING OPERATOR's key.
405
435
 
@@ -440,7 +470,7 @@ def build_attested_proof(
440
470
  capauth.exceptions.SubjectNamingError: If ``subject`` does not conform
441
471
  to the canonical fqid grammar even after alias translation.
442
472
  """
443
- fingerprint, canonical = _resolve_subject(pubkey, subject)
473
+ fingerprint, canonical = _resolve_subject(pubkey, _require_subject(subject))
444
474
  challenge = attested_challenge(fingerprint, canonical)
445
475
  signature = _sign(challenge, operator_private_key, passphrase, pgp=_is_pgp(operator_pubkey))
446
476
  _self_check(operator_pubkey, signature, challenge, what="attestation")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: capauth
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: Capability-based Authentication — decentralized, PGP-based identity and authorization
5
5
  Author-email: smilinTux <admin@smilintux.org>
6
6
  License: GPL-3.0-or-later
@@ -155,10 +155,22 @@ def test_attested_proof_round_trips_through_enroll_device(tmp_path):
155
155
 
156
156
 
157
157
  def test_helper_defaults_subject_to_the_device_fingerprint_like_enroll_device(tmp_path):
158
- """Omitting ``subject`` on both sides still agrees: both derive device:<fp>."""
158
+ """An EXPLICIT subject on both sides agrees end to end.
159
+
160
+ This test used to omit ``subject`` and rely on the builder and
161
+ ``enroll_device`` deriving the same default. That default was removed: two
162
+ individually reasonable defaults (capauth's 40-char fingerprint, skchat's
163
+ 16-char device id) produce different signed bytes, and the mismatch is
164
+ invisible until the security boundary rejects it. The subject is now
165
+ required, so the agreement is asserted rather than assumed.
166
+ """
159
167
  bundle = _pgp_keypair()
168
+ subject = fingerprint_for(bundle.public_armor)
160
169
  proof = build_verified_proof(
161
- bundle.public_armor, private_key=bundle.private_armor, passphrase=TEST_PASSPHRASE
170
+ bundle.public_armor,
171
+ private_key=bundle.private_armor,
172
+ subject=subject,
173
+ passphrase=TEST_PASSPHRASE,
162
174
  )
163
175
 
164
176
  enrollment = enroll_device(
@@ -412,11 +424,19 @@ def test_building_with_a_WRONG_PASSPHRASE_raises():
412
424
  def test_building_with_a_NON_KEY_raises_rather_than_returning_an_empty_proof():
413
425
  bundle = _pgp_keypair()
414
426
  with pytest.raises(ProofSigningError):
415
- build_verified_proof(bundle.public_armor, private_key="not a key at all")
427
+ build_verified_proof(
428
+ bundle.public_armor,
429
+ private_key="not a key at all",
430
+ subject=fingerprint_for(bundle.public_armor),
431
+ )
416
432
 
417
433
  pubkey_b64, _ = _device_keypair()
418
434
  with pytest.raises(ProofSigningError):
419
- build_verified_proof(pubkey_b64, private_key=b"-----BEGIN PRIVATE KEY-----\nnope\n")
435
+ build_verified_proof(
436
+ pubkey_b64,
437
+ private_key=b"-----BEGIN PRIVATE KEY-----\nnope\n",
438
+ subject=fingerprint_for(pubkey_b64),
439
+ )
420
440
 
421
441
 
422
442
  def test_a_built_proof_is_never_empty():
@@ -430,3 +450,65 @@ def test_a_built_proof_is_never_empty():
430
450
  assert proof.proof
431
451
  assert dict(proof) == {"proof": proof.proof}
432
452
  assert "operator_pubkey" not in dict(proof)
453
+
454
+
455
+ # --- subject is REQUIRED (regression: two reasonable defaults, different bytes) ---
456
+
457
+
458
+ def test_subject_is_required_on_every_public_builder():
459
+ """A derived default here silently changes the SIGNED BYTES.
460
+
461
+ capauth's own default was the full 40-char `fingerprint_for` value. skchat,
462
+ the helper's first caller, derives a 16-char device id. Both defensible in
463
+ isolation. But a proof built with capauth's default, then handed to
464
+ `enroll_device` with skchat's real subject, does not verify: the device signs
465
+ honestly, capauth rejects, and the enrollment lands on the TOFU floor behind
466
+ a success response with nothing raised. That is the exact silent downgrade
467
+ card N10 exists to remove, reintroduced by the convenience meant to close it.
468
+
469
+ Caught in review by clawd-43 on 2026-08-17, before any caller shipped it.
470
+ """
471
+ bundle = _pgp_keypair()
472
+
473
+ for call in (
474
+ lambda: enrollment_challenge(bundle.public_armor),
475
+ lambda: build_verified_proof(
476
+ bundle.public_armor,
477
+ private_key=bundle.private_armor,
478
+ passphrase=TEST_PASSPHRASE,
479
+ ),
480
+ ):
481
+ with pytest.raises(TypeError, match="subject"):
482
+ call()
483
+
484
+
485
+ @pytest.mark.parametrize("blank", [None, "", " "])
486
+ def test_an_explicitly_blank_subject_raises_rather_than_deriving_one(blank):
487
+ """The signature makes it required; this makes a runtime None fail too.
488
+
489
+ A caller can still thread an Optional down from its own config and hand us
490
+ None. Falling back to a derived default there is the failure this module
491
+ must never have.
492
+ """
493
+ bundle = _pgp_keypair()
494
+
495
+ with pytest.raises(ProofSigningError, match="subject is required"):
496
+ enrollment_challenge(bundle.public_armor, subject=blank)
497
+
498
+ with pytest.raises(ProofSigningError, match="subject is required"):
499
+ build_verified_proof(
500
+ bundle.public_armor,
501
+ private_key=bundle.private_armor,
502
+ subject=blank,
503
+ passphrase=TEST_PASSPHRASE,
504
+ )
505
+
506
+
507
+ def test_two_different_subjects_produce_different_challenges():
508
+ """The property the whole guard exists to protect, asserted directly."""
509
+ bundle = _pgp_keypair()
510
+ full = fingerprint_for(bundle.public_armor)
511
+
512
+ assert enrollment_challenge(bundle.public_armor, subject=full) != enrollment_challenge(
513
+ bundle.public_armor, subject=full[:16]
514
+ ), "challenge must bind the subject, or a subject mismatch would go unnoticed"
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