capauth 0.3.4__tar.gz → 0.3.6__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 (378) hide show
  1. {capauth-0.3.4 → capauth-0.3.6}/CHANGELOG.md +26 -0
  2. {capauth-0.3.4 → capauth-0.3.6}/PKG-INFO +11 -4
  3. {capauth-0.3.4 → capauth-0.3.6}/README.md +9 -3
  4. {capauth-0.3.4 → capauth-0.3.6}/SOP.md +5 -0
  5. {capauth-0.3.4 → capauth-0.3.6}/docs/ARCHITECTURE.md +3 -2
  6. {capauth-0.3.4 → capauth-0.3.6}/docs/COLD_MACHINE_BOOTSTRAP_AND_DR.md +8 -7
  7. capauth-0.3.6/docs/DELEGATED_CAPABILITIES.md +113 -0
  8. capauth-0.3.6/docs/DOWNSTREAM_DURABLE_BACKENDS.md +85 -0
  9. {capauth-0.3.4 → capauth-0.3.6}/docs/HUMAN_IDENTITY_SETUP_AND_ROTATION.md +31 -0
  10. capauth-0.3.6/docs/IDENTITY_ESTATE.md +128 -0
  11. {capauth-0.3.4 → capauth-0.3.6}/pyproject.toml +1 -0
  12. {capauth-0.3.4 → capauth-0.3.6}/scripts/provision_agent_profiles.py +25 -9
  13. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/__init__.py +69 -0
  14. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/cli.py +144 -19
  15. capauth-0.3.6/src/capauth/delegated.py +1489 -0
  16. capauth-0.3.6/src/capauth/estate.py +894 -0
  17. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/sync.py +70 -6
  18. {capauth-0.3.4 → capauth-0.3.6}/src/capauth.egg-info/PKG-INFO +11 -4
  19. {capauth-0.3.4 → capauth-0.3.6}/src/capauth.egg-info/SOURCES.txt +8 -0
  20. {capauth-0.3.4 → capauth-0.3.6}/src/capauth.egg-info/requires.txt +1 -0
  21. {capauth-0.3.4 → capauth-0.3.6}/tests/test_agent_identity.py +57 -22
  22. capauth-0.3.6/tests/test_delegated_capabilities.py +566 -0
  23. capauth-0.3.6/tests/test_estate.py +300 -0
  24. capauth-0.3.6/tests/test_sync.py +48 -0
  25. {capauth-0.3.4 → capauth-0.3.6}/.cursorrules +0 -0
  26. {capauth-0.3.4 → capauth-0.3.6}/.dockerignore +0 -0
  27. {capauth-0.3.4 → capauth-0.3.6}/.github/workflows/ci.yml +0 -0
  28. {capauth-0.3.4 → capauth-0.3.6}/.github/workflows/docs-check.yml +0 -0
  29. {capauth-0.3.4 → capauth-0.3.6}/.github/workflows/nextcloud-appstore.yml +0 -0
  30. {capauth-0.3.4 → capauth-0.3.6}/.github/workflows/publish.yml +0 -0
  31. {capauth-0.3.4 → capauth-0.3.6}/.github/workflows/pytest.yml +0 -0
  32. {capauth-0.3.4 → capauth-0.3.6}/.github/workflows/secret-scan.yml +0 -0
  33. {capauth-0.3.4 → capauth-0.3.6}/.gitignore +0 -0
  34. {capauth-0.3.4 → capauth-0.3.6}/.gitleaks-baseline.json +0 -0
  35. {capauth-0.3.4 → capauth-0.3.6}/AGENTS.md +0 -0
  36. {capauth-0.3.4 → capauth-0.3.6}/AI-ADVOCATE.md +0 -0
  37. {capauth-0.3.4 → capauth-0.3.6}/ARCHITECTURE.md +0 -0
  38. {capauth-0.3.4 → capauth-0.3.6}/CLAUDE.md +0 -0
  39. {capauth-0.3.4 → capauth-0.3.6}/CODE_OF_CONDUCT.md +0 -0
  40. {capauth-0.3.4 → capauth-0.3.6}/CONTRIBUTING.md +0 -0
  41. {capauth-0.3.4 → capauth-0.3.6}/Dockerfile +0 -0
  42. {capauth-0.3.4 → capauth-0.3.6}/Dockerfile.authentik-capauth +0 -0
  43. {capauth-0.3.4 → capauth-0.3.6}/LICENSE +0 -0
  44. {capauth-0.3.4 → capauth-0.3.6}/MANIFEST.in +0 -0
  45. {capauth-0.3.4 → capauth-0.3.6}/MISSION.md +0 -0
  46. {capauth-0.3.4 → capauth-0.3.6}/SECURITY.md +0 -0
  47. {capauth-0.3.4 → capauth-0.3.6}/SKILL.md +0 -0
  48. {capauth-0.3.4 → capauth-0.3.6}/authentik-custom/capauth_migrate.py +0 -0
  49. {capauth-0.3.4 → capauth-0.3.6}/authentik-custom/imghdr.py +0 -0
  50. {capauth-0.3.4 → capauth-0.3.6}/authentik-custom/user_settings.py +0 -0
  51. {capauth-0.3.4 → capauth-0.3.6}/bin/cli.js +0 -0
  52. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/.gitignore +0 -0
  53. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/README.md +0 -0
  54. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/RELEASING.md +0 -0
  55. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/background.js +0 -0
  56. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/content_scripts/autofill.js +0 -0
  57. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/content_scripts/cross_origin_signing.js +0 -0
  58. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/content_scripts/detector.js +0 -0
  59. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/content_scripts/provider.js +0 -0
  60. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/content_scripts/provider_bridge.js +0 -0
  61. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/icons/icon.svg +0 -0
  62. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/icons/icon128.png +0 -0
  63. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/icons/icon16.png +0 -0
  64. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/icons/icon48.png +0 -0
  65. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/lib/bunker-e2e.js +0 -0
  66. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/lib/keyqr.js +0 -0
  67. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/lib/keyvault.js +0 -0
  68. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/lib/openpgp.js +0 -0
  69. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/lib/signer-backends.js +0 -0
  70. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/manifest.firefox.json +0 -0
  71. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/manifest.json +0 -0
  72. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/native-host/capauth_signer.py +0 -0
  73. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/native-host/com.capauth.signer.json +0 -0
  74. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/native-host/install.sh +0 -0
  75. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/options/options.html +0 -0
  76. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/options/options.js +0 -0
  77. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/package-lock.json +0 -0
  78. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/package.json +0 -0
  79. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/popup/popup.css +0 -0
  80. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/popup/popup.html +0 -0
  81. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/popup/popup.js +0 -0
  82. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/scripts/build.js +0 -0
  83. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/scripts/generate-icons.js +0 -0
  84. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/stages/AuthenticatorSelectionStage.js +0 -0
  85. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/stages/CapAuthStage.js +0 -0
  86. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/stages/QRDisplayStage.js +0 -0
  87. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/stages/SignatureVerificationStage.js +0 -0
  88. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/stages/StagePipeline.js +0 -0
  89. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/submission/STORE_LISTING.md +0 -0
  90. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/submission/privacy-policy.md +0 -0
  91. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/tests/fixtures/test_key.json +0 -0
  92. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/tests/unit/background.test.js +0 -0
  93. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/tests/unit/bunker_e2e.test.js +0 -0
  94. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/tests/unit/detector.test.js +0 -0
  95. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/tests/unit/keyqr.test.js +0 -0
  96. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/tests/unit/keyvault.test.js +0 -0
  97. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/tests/unit/openpgp.test.js +0 -0
  98. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/tests/unit/provider_api.test.js +0 -0
  99. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/tests/unit/provider_tierb.test.js +0 -0
  100. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/tests/unit/remote_bunker.test.js +0 -0
  101. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/tests/unit/signer_backends.test.js +0 -0
  102. {capauth-0.3.4 → capauth-0.3.6}/browser-extension/vitest.config.js +0 -0
  103. {capauth-0.3.4 → capauth-0.3.6}/deploy/capauth-service/.env.example +0 -0
  104. {capauth-0.3.4 → capauth-0.3.6}/deploy/capauth-service/deploy.sh +0 -0
  105. {capauth-0.3.4 → capauth-0.3.6}/deploy/capauth-service/docker-compose.yml +0 -0
  106. {capauth-0.3.4 → capauth-0.3.6}/deploy/capauth-service/systemd/capauth-backup.service +0 -0
  107. {capauth-0.3.4 → capauth-0.3.6}/deploy/capauth-service/systemd/capauth-backup.timer +0 -0
  108. {capauth-0.3.4 → capauth-0.3.6}/deploy/forgejo-capauth/.env.example +0 -0
  109. {capauth-0.3.4 → capauth-0.3.6}/deploy/forgejo-capauth/docker-compose.yml +0 -0
  110. {capauth-0.3.4 → capauth-0.3.6}/deploy/forgejo-capauth/preflight.sh +0 -0
  111. {capauth-0.3.4 → capauth-0.3.6}/docs/AIRGAPPED_SIGNING_WORKSTATION.md +0 -0
  112. {capauth-0.3.4 → capauth-0.3.6}/docs/AUTHENTIK_CUSTOM_STAGE.md +0 -0
  113. {capauth-0.3.4 → capauth-0.3.6}/docs/AUTHENTIK_DEPLOYMENT_SKSSO.md +0 -0
  114. {capauth-0.3.4 → capauth-0.3.6}/docs/AUTHENTIK_FORGEJO_DEPLOYMENT.md +0 -0
  115. {capauth-0.3.4 → capauth-0.3.6}/docs/AUTHENTIK_STRATEGY_DECISION.md +0 -0
  116. {capauth-0.3.4 → capauth-0.3.6}/docs/CAPAUTH_BUNKER_REMOTE_SIGNER.md +0 -0
  117. {capauth-0.3.4 → capauth-0.3.6}/docs/CAPAUTH_OIDC_IDP.md +0 -0
  118. {capauth-0.3.4 → capauth-0.3.6}/docs/CLAIMS.md +0 -0
  119. {capauth-0.3.4 → capauth-0.3.6}/docs/CRYPTO_SPEC.md +0 -0
  120. {capauth-0.3.4 → capauth-0.3.6}/docs/ENTERPRISE_MANAGED_KEYS.md +0 -0
  121. {capauth-0.3.4 → capauth-0.3.6}/docs/INTEGRATION_BLUEPRINT.md +0 -0
  122. {capauth-0.3.4 → capauth-0.3.6}/docs/M0-capauth-api-v1.md +0 -0
  123. {capauth-0.3.4 → capauth-0.3.6}/docs/M0-capauth-importer-inventory.md +0 -0
  124. {capauth-0.3.4 → capauth-0.3.6}/docs/PQC_CAPAUTH_REVISIT.md +0 -0
  125. {capauth-0.3.4 → capauth-0.3.6}/docs/PQC_ROOT_MIGRATION.md +0 -0
  126. {capauth-0.3.4 → capauth-0.3.6}/docs/PROTOCOL.md +0 -0
  127. {capauth-0.3.4 → capauth-0.3.6}/docs/ROOT_ROTATION_CEREMONY.md +0 -0
  128. {capauth-0.3.4 → capauth-0.3.6}/docs/TRUST_GRAPH_COORD_PROJECTION.md +0 -0
  129. {capauth-0.3.4 → capauth-0.3.6}/docs/ZERO_KNOWLEDGE_PROFILE.md +0 -0
  130. {capauth-0.3.4 → capauth-0.3.6}/docs/authentik-capauth.md +0 -0
  131. {capauth-0.3.4 → capauth-0.3.6}/docs/deploy-plan/capauth-bulletproof-deploy.md +0 -0
  132. {capauth-0.3.4 → capauth-0.3.6}/index.d.ts +0 -0
  133. {capauth-0.3.4 → capauth-0.3.6}/index.js +0 -0
  134. {capauth-0.3.4 → capauth-0.3.6}/openclaw-plugin.archived-2026-04-23/package.json +0 -0
  135. {capauth-0.3.4 → capauth-0.3.6}/openclaw-plugin.archived-2026-04-23/src/index.ts +0 -0
  136. {capauth-0.3.4 → capauth-0.3.6}/openclaw-plugin.archived-2026-04-23/src/openclaw.plugin.json +0 -0
  137. {capauth-0.3.4 → capauth-0.3.6}/package.json +0 -0
  138. {capauth-0.3.4 → capauth-0.3.6}/phone-signer/app.js +0 -0
  139. {capauth-0.3.4 → capauth-0.3.6}/phone-signer/icons/icon-192.png +0 -0
  140. {capauth-0.3.4 → capauth-0.3.6}/phone-signer/icons/icon-512.png +0 -0
  141. {capauth-0.3.4 → capauth-0.3.6}/phone-signer/index.html +0 -0
  142. {capauth-0.3.4 → capauth-0.3.6}/phone-signer/lib/bunker-e2e.js +0 -0
  143. {capauth-0.3.4 → capauth-0.3.6}/phone-signer/lib/bunker-signer.js +0 -0
  144. {capauth-0.3.4 → capauth-0.3.6}/phone-signer/lib/canonical.js +0 -0
  145. {capauth-0.3.4 → capauth-0.3.6}/phone-signer/lib/keyqr.js +0 -0
  146. {capauth-0.3.4 → capauth-0.3.6}/phone-signer/lib/keyvault.js +0 -0
  147. {capauth-0.3.4 → capauth-0.3.6}/phone-signer/manifest.webmanifest +0 -0
  148. {capauth-0.3.4 → capauth-0.3.6}/phone-signer/sw.js +0 -0
  149. {capauth-0.3.4 → capauth-0.3.6}/phone-signer/vendor/openpgp.min.js +0 -0
  150. {capauth-0.3.4 → capauth-0.3.6}/phone-signer/vendor/qrcode-generator.js +0 -0
  151. {capauth-0.3.4 → capauth-0.3.6}/ruff-0.15.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl +0 -0
  152. {capauth-0.3.4 → capauth-0.3.6}/scripts/build_authentik_capauth_image.sh +0 -0
  153. {capauth-0.3.4 → capauth-0.3.6}/scripts/capauth-backup.sh +0 -0
  154. {capauth-0.3.4 → capauth-0.3.6}/scripts/capauth-restore.sh +0 -0
  155. {capauth-0.3.4 → capauth-0.3.6}/scripts/migrate_canonical_subjects.py +0 -0
  156. {capauth-0.3.4 → capauth-0.3.6}/scripts/pqc_ceremony_dryrun.py +0 -0
  157. {capauth-0.3.4 → capauth-0.3.6}/setup.cfg +0 -0
  158. {capauth-0.3.4 → capauth-0.3.6}/skill.yaml +0 -0
  159. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/agent_identity.py +0 -0
  160. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/apps.py +0 -0
  161. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/authentik/__init__.py +0 -0
  162. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/authentik/api.py +0 -0
  163. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/authentik/blueprints/capauth-pgp-login.yaml +0 -0
  164. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/authentik/claims_mapper.py +0 -0
  165. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/authentik/nonce_store.py +0 -0
  166. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/authentik/stage.py +0 -0
  167. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/authentik/urls.py +0 -0
  168. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/authentik/verifier.py +0 -0
  169. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/authz.py +0 -0
  170. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/crypto/__init__.py +0 -0
  171. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/crypto/base.py +0 -0
  172. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/crypto/gnupg_backend.py +0 -0
  173. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/crypto/pgpy_backend.py +0 -0
  174. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/crypto/sequoia_backend.py +0 -0
  175. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/crypto/skpgp_backend.py +0 -0
  176. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/custody.py +0 -0
  177. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/did.py +0 -0
  178. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/discovery/__init__.py +0 -0
  179. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/discovery/base.py +0 -0
  180. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/discovery/file_discovery.py +0 -0
  181. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/discovery/mdns.py +0 -0
  182. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/discovery/syncthing.py +0 -0
  183. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/exceptions.py +0 -0
  184. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/identity.py +0 -0
  185. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/identity_class.py +0 -0
  186. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integration.py +0 -0
  187. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/__init__.py +0 -0
  188. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/forgejo/__init__.py +0 -0
  189. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/forgejo/auth_flow.py +0 -0
  190. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/forgejo/cli.py +0 -0
  191. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/forgejo/config.py +0 -0
  192. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/forgejo/forgejo_api.py +0 -0
  193. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/forgejo/oidc_provider.py +0 -0
  194. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/forgejo/tests/__init__.py +0 -0
  195. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/forgejo/tests/test_auth_flow.py +0 -0
  196. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/forgejo/tests/test_cli.py +0 -0
  197. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/forgejo/tests/test_config.py +0 -0
  198. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/forgejo/tests/test_forgejo_api.py +0 -0
  199. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/forgejo/tests/test_oidc_provider.py +0 -0
  200. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/.gitignore +0 -0
  201. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/.nextcloudignore +0 -0
  202. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/CSR-PR.md +0 -0
  203. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/INFO-XML-CHANGES.md +0 -0
  204. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/PUBLISHING.md +0 -0
  205. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/README.md +0 -0
  206. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/appinfo/info.xml +0 -0
  207. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/appinfo/routes.php +0 -0
  208. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/build-release.sh +0 -0
  209. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/certificates/.gitignore +0 -0
  210. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/certificates/capauth.crt +0 -0
  211. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/certificates/capauth.csr +0 -0
  212. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/composer.json +0 -0
  213. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/css/login.css +0 -0
  214. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/js/login.js +0 -0
  215. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/krankerl.toml +0 -0
  216. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/AlternativeLogin/CapAuthLogin.php +0 -0
  217. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/AppInfo/Application.php +0 -0
  218. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/BackgroundJob/PruneExpiredNonces.php +0 -0
  219. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/Controller/LoginController.php +0 -0
  220. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/Controller/TokenController.php +0 -0
  221. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/Db/KeyRegistry.php +0 -0
  222. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/Exception/CapAuthUnauthorizedException.php +0 -0
  223. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/Middleware/PgpVerificationMiddleware.php +0 -0
  224. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/Migration/Version000300Date20260301000000.php +0 -0
  225. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/Provider/CapAuthProvider.php +0 -0
  226. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/Service/ChallengeService.php +0 -0
  227. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/Service/GroupSyncService.php +0 -0
  228. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/Service/UserProvisioningService.php +0 -0
  229. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/Service/VerifierService.php +0 -0
  230. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/User/Backend.php +0 -0
  231. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/phpunit.xml +0 -0
  232. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/screenshots/README.md +0 -0
  233. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/screenshots/login.png +0 -0
  234. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/templates/login.php +0 -0
  235. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/tests/bootstrap.php +0 -0
  236. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/tests/integration/FullAuthFlowTest.php +0 -0
  237. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/tests/oidc/oidc_e2e_login_test.py +0 -0
  238. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/tests/unit/Controller/LoginControllerTest.php +0 -0
  239. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/tests/unit/Db/KeyRegistryTest.php +0 -0
  240. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/tests/unit/Provider/CapAuthProviderTest.php +0 -0
  241. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/tests/unit/Service/ChallengeServiceTest.php +0 -0
  242. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/tests/unit/Service/VerifierServiceTest.php +0 -0
  243. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/tests/unit/User/BackendTest.php +0 -0
  244. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/key_transparency.py +0 -0
  245. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/login.py +0 -0
  246. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/manifest.py +0 -0
  247. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/mesh.py +0 -0
  248. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/migrations/0001_initial.py +0 -0
  249. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/migrations/0002_widen_fingerprint_to_64.py +0 -0
  250. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/migrations/__init__.py +0 -0
  251. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/models.py +0 -0
  252. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pairing/__init__.py +0 -0
  253. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pairing/canonicalize.py +0 -0
  254. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pairing/kernel.py +0 -0
  255. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pairing/operator_session.py +0 -0
  256. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pairing/proof.py +0 -0
  257. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pairing/records.py +0 -0
  258. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pairing/store.py +0 -0
  259. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pairing/window.py +0 -0
  260. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pma.py +0 -0
  261. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pqc_confidentiality.py +0 -0
  262. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pqc_identity.py +0 -0
  263. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pqc_root_identity.py +0 -0
  264. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pqc_tokens.py +0 -0
  265. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/profile.py +0 -0
  266. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/provisioning.py +0 -0
  267. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/registry.py +0 -0
  268. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/seal.py +0 -0
  269. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/__init__.py +0 -0
  270. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/app.py +0 -0
  271. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/bunker.py +0 -0
  272. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/bunker_e2e.py +0 -0
  273. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/keystore.py +0 -0
  274. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/oidc/__init__.py +0 -0
  275. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/oidc/clients.example.json +0 -0
  276. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/oidc/clients.py +0 -0
  277. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/oidc/passkey.py +0 -0
  278. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/oidc/provider.py +0 -0
  279. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/oidc/signing_key.py +0 -0
  280. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/oidc/store.py +0 -0
  281. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/push.py +0 -0
  282. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/server.py +0 -0
  283. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/subject.py +0 -0
  284. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/testing.py +0 -0
  285. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/tokens.py +0 -0
  286. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/trust/__init__.py +0 -0
  287. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/trust/calibration.py +0 -0
  288. {capauth-0.3.4 → capauth-0.3.6}/src/capauth/trust/graph.py +0 -0
  289. {capauth-0.3.4 → capauth-0.3.6}/src/capauth.egg-info/dependency_links.txt +0 -0
  290. {capauth-0.3.4 → capauth-0.3.6}/src/capauth.egg-info/entry_points.txt +0 -0
  291. {capauth-0.3.4 → capauth-0.3.6}/src/capauth.egg-info/top_level.txt +0 -0
  292. {capauth-0.3.4 → capauth-0.3.6}/stages/ak-stage-capauth/.gitignore +0 -0
  293. {capauth-0.3.4 → capauth-0.3.6}/stages/ak-stage-capauth/package-lock.json +0 -0
  294. {capauth-0.3.4 → capauth-0.3.6}/stages/ak-stage-capauth/package.json +0 -0
  295. {capauth-0.3.4 → capauth-0.3.6}/stages/ak-stage-capauth/src/CapAuthStage.test.js +0 -0
  296. {capauth-0.3.4 → capauth-0.3.6}/stages/ak-stage-capauth/src/CapAuthStage.vue +0 -0
  297. {capauth-0.3.4 → capauth-0.3.6}/stages/ak-stage-capauth/src/index.js +0 -0
  298. {capauth-0.3.4 → capauth-0.3.6}/stages/ak-stage-capauth/vite.config.js +0 -0
  299. {capauth-0.3.4 → capauth-0.3.6}/tests/__init__.py +0 -0
  300. {capauth-0.3.4 → capauth-0.3.6}/tests/conftest.py +0 -0
  301. {capauth-0.3.4 → capauth-0.3.6}/tests/fixtures/bunker_e2e_v1_vector.json +0 -0
  302. {capauth-0.3.4 → capauth-0.3.6}/tests/fixtures/canonical_nonce_v2_vector.json +0 -0
  303. {capauth-0.3.4 → capauth-0.3.6}/tests/test_agentrun_capabilities.py +0 -0
  304. {capauth-0.3.4 → capauth-0.3.6}/tests/test_audience_mint_helper.py +0 -0
  305. {capauth-0.3.4 → capauth-0.3.6}/tests/test_audience_tokens.py +0 -0
  306. {capauth-0.3.4 → capauth-0.3.6}/tests/test_authz.py +0 -0
  307. {capauth-0.3.4 → capauth-0.3.6}/tests/test_authz_legacy_grace.py +0 -0
  308. {capauth-0.3.4 → capauth-0.3.6}/tests/test_authz_signature_gate.py +0 -0
  309. {capauth-0.3.4 → capauth-0.3.6}/tests/test_backup_restore.py +0 -0
  310. {capauth-0.3.4 → capauth-0.3.6}/tests/test_bunker.py +0 -0
  311. {capauth-0.3.4 → capauth-0.3.6}/tests/test_bunker_e2e.py +0 -0
  312. {capauth-0.3.4 → capauth-0.3.6}/tests/test_canonicalize.py +0 -0
  313. {capauth-0.3.4 → capauth-0.3.6}/tests/test_capauth_service_deploy.py +0 -0
  314. {capauth-0.3.4 → capauth-0.3.6}/tests/test_change_capabilities.py +0 -0
  315. {capauth-0.3.4 → capauth-0.3.6}/tests/test_claims_mapper.py +0 -0
  316. {capauth-0.3.4 → capauth-0.3.6}/tests/test_crypto.py +0 -0
  317. {capauth-0.3.4 → capauth-0.3.6}/tests/test_custody.py +0 -0
  318. {capauth-0.3.4 → capauth-0.3.6}/tests/test_device_key_proof.py +0 -0
  319. {capauth-0.3.4 → capauth-0.3.6}/tests/test_did_capability_relationships.py +0 -0
  320. {capauth-0.3.4 → capauth-0.3.6}/tests/test_enrollment_proof_helper.py +0 -0
  321. {capauth-0.3.4 → capauth-0.3.6}/tests/test_fingerprint_v6.py +0 -0
  322. {capauth-0.3.4 → capauth-0.3.6}/tests/test_forgejo_preflight.py +0 -0
  323. {capauth-0.3.4 → capauth-0.3.6}/tests/test_home_resolution.py +0 -0
  324. {capauth-0.3.4 → capauth-0.3.6}/tests/test_identity.py +0 -0
  325. {capauth-0.3.4 → capauth-0.3.6}/tests/test_identity_class.py +0 -0
  326. {capauth-0.3.4 → capauth-0.3.6}/tests/test_identity_ttl.py +0 -0
  327. {capauth-0.3.4 → capauth-0.3.6}/tests/test_integration.py +0 -0
  328. {capauth-0.3.4 → capauth-0.3.6}/tests/test_key_revocation_expiry.py +0 -0
  329. {capauth-0.3.4 → capauth-0.3.6}/tests/test_key_transparency.py +0 -0
  330. {capauth-0.3.4 → capauth-0.3.6}/tests/test_login.py +0 -0
  331. {capauth-0.3.4 → capauth-0.3.6}/tests/test_manifest.py +0 -0
  332. {capauth-0.3.4 → capauth-0.3.6}/tests/test_mesh.py +0 -0
  333. {capauth-0.3.4 → capauth-0.3.6}/tests/test_models_pqc_q0.py +0 -0
  334. {capauth-0.3.4 → capauth-0.3.6}/tests/test_nextcloud_app.py +0 -0
  335. {capauth-0.3.4 → capauth-0.3.6}/tests/test_no_subapp_dependency.py +0 -0
  336. {capauth-0.3.4 → capauth-0.3.6}/tests/test_nonce_store.py +0 -0
  337. {capauth-0.3.4 → capauth-0.3.6}/tests/test_oidc_idp.py +0 -0
  338. {capauth-0.3.4 → capauth-0.3.6}/tests/test_oidc_idp_e2e.py +0 -0
  339. {capauth-0.3.4 → capauth-0.3.6}/tests/test_operator_session.py +0 -0
  340. {capauth-0.3.4 → capauth-0.3.6}/tests/test_pairing.py +0 -0
  341. {capauth-0.3.4 → capauth-0.3.6}/tests/test_pairing_base_dir_env.py +0 -0
  342. {capauth-0.3.4 → capauth-0.3.6}/tests/test_passkey.py +0 -0
  343. {capauth-0.3.4 → capauth-0.3.6}/tests/test_pma.py +0 -0
  344. {capauth-0.3.4 → capauth-0.3.6}/tests/test_pqc_confidentiality.py +0 -0
  345. {capauth-0.3.4 → capauth-0.3.6}/tests/test_pqc_identity.py +0 -0
  346. {capauth-0.3.4 → capauth-0.3.6}/tests/test_pqc_root_integration.py +0 -0
  347. {capauth-0.3.4 → capauth-0.3.6}/tests/test_pqc_t3_gate.py +0 -0
  348. {capauth-0.3.4 → capauth-0.3.6}/tests/test_pqc_tokens.py +0 -0
  349. {capauth-0.3.4 → capauth-0.3.6}/tests/test_profile.py +0 -0
  350. {capauth-0.3.4 → capauth-0.3.6}/tests/test_provisioning.py +0 -0
  351. {capauth-0.3.4 → capauth-0.3.6}/tests/test_push.py +0 -0
  352. {capauth-0.3.4 → capauth-0.3.6}/tests/test_py313_compat.py +0 -0
  353. {capauth-0.3.4 → capauth-0.3.6}/tests/test_qr_login.py +0 -0
  354. {capauth-0.3.4 → capauth-0.3.6}/tests/test_real_pgp_e2e.py +0 -0
  355. {capauth-0.3.4 → capauth-0.3.6}/tests/test_register_cli.py +0 -0
  356. {capauth-0.3.4 → capauth-0.3.6}/tests/test_registry.py +0 -0
  357. {capauth-0.3.4 → capauth-0.3.6}/tests/test_seal.py +0 -0
  358. {capauth-0.3.4 → capauth-0.3.6}/tests/test_sequoia_backend.py +0 -0
  359. {capauth-0.3.4 → capauth-0.3.6}/tests/test_service.py +0 -0
  360. {capauth-0.3.4 → capauth-0.3.6}/tests/test_service_authz_decide.py +0 -0
  361. {capauth-0.3.4 → capauth-0.3.6}/tests/test_service_v6_fingerprint.py +0 -0
  362. {capauth-0.3.4 → capauth-0.3.6}/tests/test_skpgp_backend.py +0 -0
  363. {capauth-0.3.4 → capauth-0.3.6}/tests/test_subject.py +0 -0
  364. {capauth-0.3.4 → capauth-0.3.6}/tests/test_syncthing_discovery.py +0 -0
  365. {capauth-0.3.4 → capauth-0.3.6}/tests/test_testing_helper.py +0 -0
  366. {capauth-0.3.4 → capauth-0.3.6}/tests/test_token_gc.py +0 -0
  367. {capauth-0.3.4 → capauth-0.3.6}/tests/test_token_store_flag.py +0 -0
  368. {capauth-0.3.4 → capauth-0.3.6}/tests/test_tokens.py +0 -0
  369. {capauth-0.3.4 → capauth-0.3.6}/tests/test_trust_calibration.py +0 -0
  370. {capauth-0.3.4 → capauth-0.3.6}/tests/test_trust_graph.py +0 -0
  371. {capauth-0.3.4 → capauth-0.3.6}/tests/test_trust_graph_coord_audit.py +0 -0
  372. {capauth-0.3.4 → capauth-0.3.6}/tests/test_verifier.py +0 -0
  373. {capauth-0.3.4 → capauth-0.3.6}/tests/test_version_honesty.py +0 -0
  374. {capauth-0.3.4 → capauth-0.3.6}/tests/test_zk_profile.py +0 -0
  375. {capauth-0.3.4 → capauth-0.3.6}/tools/build-sq.sh +0 -0
  376. {capauth-0.3.4 → capauth-0.3.6}/urllib.error +0 -0
  377. {capauth-0.3.4 → capauth-0.3.6}/web/README.md +0 -0
  378. {capauth-0.3.4 → capauth-0.3.6}/web/stages/capauth/CapAuthStage.ts +0 -0
@@ -6,6 +6,11 @@ All notable changes to `capauth` are documented here. The format is based on
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ - Recorded the verified 2026-08-20 Chef custody/signing ceremony and the
10
+ deferred passphrase-disclosure response (`da8a6401`) in the human identity
11
+ runbook, including the safe hidden-input pattern and honest residual-risk
12
+ language.
13
+
9
14
  ### Documentation
10
15
 
11
16
  - Added the SK-standards-aligned human identity setup and rotation runbook,
@@ -15,6 +20,11 @@ All notable changes to `capauth` are documented here. The format is based on
15
20
 
16
21
  ### Fixed
17
22
 
23
+ - Restored the declared Python 3.10 support for delegated capabilities by using
24
+ compatible UTC, string-enum, `Never`, and `Self` definitions. Downstream
25
+ SKVault installs can again complete their full 3.10 test gate.
26
+ - Restored Ruff formatting for the delegated-capability and CLI modules so the
27
+ enforced lint gate agrees with the released source.
18
28
  - **Identity docstrings taught the wrong fqid shape.** `agent_identity.py`
19
29
  showed `lumina@chef.skworld` in its prose, its worked example and its
20
30
  doctest: the missing-TLD form that the N5 store migration exists to correct.
@@ -25,6 +35,22 @@ All notable changes to `capauth` are documented here. The format is based on
25
35
 
26
36
  ### Added
27
37
 
38
+ - **A strict reusable delegated capability-chain contract.**
39
+ `capauth.delegated` adds a versioned request-local transport, complete-chain
40
+ verification, bounded monotonic attenuation, current issuer and principal
41
+ policy checks for every chain member, ancestor and leaf revocation, atomic
42
+ one-use replay reservation, and exact-scope sanitized decisions. Recursive
43
+ duplicate JSON members, unknown fields, ambiguous envelopes, missing or
44
+ reordered ancestors, over-depth chains, broadened children, stale principals,
45
+ untrusted issuers, revoked credentials, expired credentials, and replay all
46
+ fail closed. Expiry and current-state snapshots are refreshed after signature,
47
+ replay, and durable audit work before allow. Any policy revision change during
48
+ audit denies and requires a fresh invocation. Production storage remains application supplied;
49
+ included in-memory backends are explicitly limited to tests and isolated local
50
+ development. Raw credentials and signature bytes are redacted from values,
51
+ decisions, audit records, and chained exceptions. See
52
+ `docs/DELEGATED_CAPABILITIES.md`.
53
+
28
54
  - **`capauth.pairing.proof`: a supported way to BUILD the enrollment proof card
29
55
  N10 made mandatory.** `83c1fa2` made `proof` required for `verified` and
30
56
  `operator_pubkey` + `attestation` required for `attested`, but shipped the
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: capauth
3
- Version: 0.3.4
3
+ Version: 0.3.6
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
@@ -29,6 +29,7 @@ Requires-Dist: PGPy>=0.6
29
29
  Requires-Dist: pyyaml>=6.0
30
30
  Requires-Dist: rich>=13.0
31
31
  Requires-Dist: httpx>=0.25
32
+ Requires-Dist: typing-extensions>=4.8
32
33
  Provides-Extra: skcapstone
33
34
  Requires-Dist: skcapstone>=0.6.8; extra == "skcapstone"
34
35
  Provides-Extra: gnupg
@@ -182,8 +183,11 @@ capauth login https://forgejo.local # passwordless PGP login to a serv
182
183
  ```
183
184
 
184
185
  Your keypair and profile live at `~/.capauth/` — on your machine, under your
185
- keys. Use `--sync` on `init` (or `capauth sync`) to replicate the identity across
186
- all Syncthing mesh nodes so every host shares one keypair.
186
+ keys. Use `--sync` on `init` (or `capauth sync`) to distribute the public
187
+ identity across Syncthing mesh nodes. CapAuth writes folder-root exclusions
188
+ before enabling sync so private keys, root revocation certificates, keystores,
189
+ backups, passphrases, and environment files remain local. Provision scoped
190
+ service/node signers separately; do not use public sync as private-key recovery.
187
191
 
188
192
  ## What capauth provides
189
193
 
@@ -197,6 +201,7 @@ all Syncthing mesh nodes so every host shares one keypair.
197
201
  | **Verification service** | A FastAPI service that turns a signed challenge into OIDC claims — passwordless PGP login for any OIDC app (`capauth-service`) |
198
202
  | **Peer mesh** | Discover and verify sovereign peers over mDNS, shared filesystem, and Syncthing — no servers (`capauth mesh`, `discover`, `peers`) |
199
203
  | **PMA membership** | Fiducia Communitatis — PGP-signed, steward-countersigned membership claims (`capauth pma request/approve/verify/revoke`) |
204
+ | **Delegated capabilities** | Strict complete-chain validation with current issuer, principal, revocation, replay, attenuation, exact-scope, and sanitized-decision interfaces (`capauth.delegated`) |
200
205
  | **Org registry** | Register with a sovereign org; emits a signed registry entry + PMA request (`capauth register`) |
201
206
  | **Integration generators** | One-shot config for third-party login, e.g. Forgejo OAuth2/OIDC (`capauth setup forgejo`) |
202
207
  | **skcapstone adapter** | Default-on-by-presence: routes auth events to `sk-alert`, registers a key-rotation check with `skscheduler` |
@@ -233,7 +238,8 @@ verification service uses).
233
238
  capauth init --name "Chef" --email "..." # create sovereign profile (PGP keypair)
234
239
  capauth profile show | verify # display / verify signature integrity
235
240
  capauth export-pubkey [-o file.asc] # export ASCII-armored public key
236
- capauth sync # replicate ~/.capauth/ across Syncthing mesh
241
+ capauth sync # distribute public identity; keep secrets local
242
+ capauth doctor estate --manifest estate.json # find legacy/retired/conflict key copies
237
243
 
238
244
  # Verification
239
245
  capauth verify --pubkey peer.pub.asc # challenge-response round-trip
@@ -283,6 +289,7 @@ rides in the payload `event` field so routing stays severity-based.
283
289
  | **[Crypto Spec](docs/CRYPTO_SPEC.md)** | PGP implementation, key management, challenge-response details |
284
290
  | **[Protocol](docs/PROTOCOL.md)** | the CapAuth wire protocol specification |
285
291
  | **[Claims](docs/CLAIMS.md)** | capability claims and token format |
292
+ | **[Strict delegated capabilities](docs/DELEGATED_CAPABILITIES.md)** | versioned chain transport, verification invariants, backend contracts, and application composition |
286
293
  | **[Integration Blueprint](docs/INTEGRATION_BLUEPRINT.md)** | third-party integration guide |
287
294
  | **[Cold-Machine Bootstrap & DR](docs/COLD_MACHINE_BOOTSTRAP_AND_DR.md)** | standing capauth back up on a blank box + disaster recovery: the restore-not-regenerate rule, the ordered restore chain, and the operator checklist |
288
295
  | **[authentik-capauth](docs/authentik-capauth.md)** | the custom Authentik image with the CapAuth PGP stage baked in — build (`AK_VERSION`, version-agnostic venv install, frontend rebuild), SKStacks deploy + `lifecycle.migrate` override, and the four build/migrate gotchas |
@@ -120,8 +120,11 @@ capauth login https://forgejo.local # passwordless PGP login to a serv
120
120
  ```
121
121
 
122
122
  Your keypair and profile live at `~/.capauth/` — on your machine, under your
123
- keys. Use `--sync` on `init` (or `capauth sync`) to replicate the identity across
124
- all Syncthing mesh nodes so every host shares one keypair.
123
+ keys. Use `--sync` on `init` (or `capauth sync`) to distribute the public
124
+ identity across Syncthing mesh nodes. CapAuth writes folder-root exclusions
125
+ before enabling sync so private keys, root revocation certificates, keystores,
126
+ backups, passphrases, and environment files remain local. Provision scoped
127
+ service/node signers separately; do not use public sync as private-key recovery.
125
128
 
126
129
  ## What capauth provides
127
130
 
@@ -135,6 +138,7 @@ all Syncthing mesh nodes so every host shares one keypair.
135
138
  | **Verification service** | A FastAPI service that turns a signed challenge into OIDC claims — passwordless PGP login for any OIDC app (`capauth-service`) |
136
139
  | **Peer mesh** | Discover and verify sovereign peers over mDNS, shared filesystem, and Syncthing — no servers (`capauth mesh`, `discover`, `peers`) |
137
140
  | **PMA membership** | Fiducia Communitatis — PGP-signed, steward-countersigned membership claims (`capauth pma request/approve/verify/revoke`) |
141
+ | **Delegated capabilities** | Strict complete-chain validation with current issuer, principal, revocation, replay, attenuation, exact-scope, and sanitized-decision interfaces (`capauth.delegated`) |
138
142
  | **Org registry** | Register with a sovereign org; emits a signed registry entry + PMA request (`capauth register`) |
139
143
  | **Integration generators** | One-shot config for third-party login, e.g. Forgejo OAuth2/OIDC (`capauth setup forgejo`) |
140
144
  | **skcapstone adapter** | Default-on-by-presence: routes auth events to `sk-alert`, registers a key-rotation check with `skscheduler` |
@@ -171,7 +175,8 @@ verification service uses).
171
175
  capauth init --name "Chef" --email "..." # create sovereign profile (PGP keypair)
172
176
  capauth profile show | verify # display / verify signature integrity
173
177
  capauth export-pubkey [-o file.asc] # export ASCII-armored public key
174
- capauth sync # replicate ~/.capauth/ across Syncthing mesh
178
+ capauth sync # distribute public identity; keep secrets local
179
+ capauth doctor estate --manifest estate.json # find legacy/retired/conflict key copies
175
180
 
176
181
  # Verification
177
182
  capauth verify --pubkey peer.pub.asc # challenge-response round-trip
@@ -221,6 +226,7 @@ rides in the payload `event` field so routing stays severity-based.
221
226
  | **[Crypto Spec](docs/CRYPTO_SPEC.md)** | PGP implementation, key management, challenge-response details |
222
227
  | **[Protocol](docs/PROTOCOL.md)** | the CapAuth wire protocol specification |
223
228
  | **[Claims](docs/CLAIMS.md)** | capability claims and token format |
229
+ | **[Strict delegated capabilities](docs/DELEGATED_CAPABILITIES.md)** | versioned chain transport, verification invariants, backend contracts, and application composition |
224
230
  | **[Integration Blueprint](docs/INTEGRATION_BLUEPRINT.md)** | third-party integration guide |
225
231
  | **[Cold-Machine Bootstrap & DR](docs/COLD_MACHINE_BOOTSTRAP_AND_DR.md)** | standing capauth back up on a blank box + disaster recovery: the restore-not-regenerate rule, the ordered restore chain, and the operator checklist |
226
232
  | **[authentik-capauth](docs/authentik-capauth.md)** | the custom Authentik image with the CapAuth PGP stage baked in — build (`AK_VERSION`, version-agnostic venv install, frontend rebuild), SKStacks deploy + `lifecycle.migrate` override, and the four build/migrate gotchas |
@@ -182,6 +182,11 @@ its `integration` job is `continue-on-error: true`, so that job is red-tolerant
182
182
  design. `ci.yml` is the honest gate. Add the `skcapstone doctor` identity invariants
183
183
  on top when the change touches the resolver.
184
184
 
185
+ The Python 3.10 leg is a supported compatibility boundary, not an advisory job.
186
+ Shared modules must use `typing_extensions` for post-3.10 typing features and
187
+ portable UTC and string-enum definitions. A new feature is not releasable if
188
+ importing it breaks a supported matrix interpreter.
189
+
185
190
  ---
186
191
 
187
192
  ## 5. Release / Deploy
@@ -45,7 +45,7 @@ flowchart TD
45
45
  INIT["capauth init<br/>--name --email --type --algorithm"] --> GEN["crypto backend generates keypair<br/>(pgpy default, or gnupg)"]
46
46
  GEN --> WRITE["write sovereign profile<br/>~/.capauth/identity/ (public + private armor)<br/>profile.json (signed)"]
47
47
  WRITE --> SYNC{"--sync?"}
48
- SYNC -->|yes, Syncthing present| REPL["replicate ~/.capauth/<br/>across all mesh nodes"]
48
+ SYNC -->|yes, Syncthing present| REPL["distribute public identity<br/>secrets remain local"]
49
49
  SYNC -->|no| LOCAL["identity stays local"]
50
50
  WRITE --> USE["profile.json = source of truth"]
51
51
  USE --> SHOW["capauth profile show"]
@@ -273,7 +273,8 @@ flowchart LR
273
273
  | `discovery/` | Discovery backends — `file_discovery.py`, `mdns.py`, `syncthing.py` |
274
274
  | `pma.py` | PMA membership — request / approve / verify / revoke, `MembershipClaim` |
275
275
  | `registry.py` | `RegistryEntry`, sovereign org registry, `build_capauth_uri` |
276
- | `sync.py` | Syncthing identity replication helpers |
276
+ | `sync.py` | Syncthing public-identity distribution with secret exclusions |
277
+ | `estate.py` | Estate manifest, alternate-home/keyring discovery, retirement gate, evidence |
277
278
  | `integration.py` | skcapstone adapter — `alert()` (sk-alert), `ensure_schedule()` (skscheduler), `register_self()`; default-on-by-presence |
278
279
  | `apps.py` | App/service descriptor helpers |
279
280
  | `integrations/forgejo/` | Forgejo OIDC provider + auth flow + config generator |
@@ -242,10 +242,10 @@ new material to the classical root or to a per-agent key.
242
242
 
243
243
  > ### STOP - REQUIRES CHEF (private key files)
244
244
 
245
- Restore the operator/root identity directory from the sovereign backup. This is
246
- the Phase-0 tarball from the ceremony doc, or the Syncthing-replicated
247
- `~/.capauth/` (capauth replicates the identity across mesh nodes via `capauth
248
- sync` / `--sync`).
245
+ Restore the operator/root identity directory from the encrypted sovereign
246
+ custody backup. Syncthing public-identity distribution (`capauth sync` /
247
+ `--sync`) deliberately excludes `private.*` and `root-revocation.asc`; it is not
248
+ a private-key recovery source.
249
249
 
250
250
  ```sh
251
251
  # from the backup tarball (created per ROOT_ROTATION_CEREMONY.md Phase 0):
@@ -256,9 +256,10 @@ capauth profile show # confirm entity + fingerprint
256
256
  capauth profile verify # PGP self-signature must verify
257
257
  ```
258
258
 
259
- If you replicate via Syncthing instead, let `~/.capauth/` sync in, then run the
260
- same two verify commands. Do **not** run `capauth init` on this machine - that
261
- generates a *new* keypair and would fork the operator identity.
259
+ After restoring the private key from encrypted custody, public Syncthing state
260
+ may fill in non-secret profile material. Run the same two verify commands. Do
261
+ **not** run `capauth init` on this machine - that generates a *new* keypair and
262
+ would fork the operator identity.
262
263
 
263
264
  ### Step 5 - Restore agent profiles and identity.json (do NOT mint)
264
265
 
@@ -0,0 +1,113 @@
1
+ # Strict delegated capabilities
2
+
3
+ `capauth.delegated` is a reusable verification contract for short-lived,
4
+ one-use capability chains. It is a library boundary, not an authorization
5
+ server. An application still owns its policy, current principal registry,
6
+ issuer policy, revocation store, durable replay reservation, and audit sink.
7
+
8
+ The contract verifies all authority presented for one protected invocation:
9
+
10
+ - a versioned transport contains the leaf and the complete ordered ancestor chain;
11
+ - every token uses the existing issuer-pinned CapAuth signed-token format;
12
+ - every root, ancestor, leaf, and authenticated request principal must match a
13
+ current active principal-policy snapshot;
14
+ - every issuer must be currently trusted for the exact capability, audience,
15
+ and principal kind it signed;
16
+ - every child names the exact parent credential digest, stays within the signed
17
+ depth bound, keeps all exact scope fields, and can only add constraints or
18
+ narrow an unbound resource identifier;
19
+ - revocation is checked for the leaf and every ancestor;
20
+ - the leaf is atomically reserved before allow, making it one-use;
21
+ - expiry is checked before and after signature work, after replay reservation,
22
+ and after durable audit immediately before returning allow;
23
+ - issuer, principal, and revocation state is refreshed both before and after
24
+ audit; any otherwise-valid policy revision change during audit denies and
25
+ requires a fresh invocation;
26
+ - allow and deny results contain exact scope and policy revisions, but never raw
27
+ credentials, signatures, token metadata, or exception causes.
28
+
29
+ ## Wire contract
30
+
31
+ A direct root token may be presented as its exact `skcapstone_token` JSON.
32
+ Delegated authority uses this envelope:
33
+
34
+ ```json
35
+ {
36
+ "capauth_presented_capability": "1.0",
37
+ "chain": {
38
+ "ancestors": ["<root token>", "<optional parent token>"],
39
+ "leaf": "<leaf token>"
40
+ }
41
+ }
42
+ ```
43
+
44
+ `parse_authorization_bearer()` rejects unknown fields, wrong versions,
45
+ over-depth or repeated credentials, oversized values, ambiguous shapes, and
46
+ duplicate JSON object members at every parsed depth. `PresentedCapability`
47
+ redacts `str()` and `repr()` and refuses serialization so bearer bytes remain
48
+ request-local.
49
+
50
+ An audit sink must preserve append order for a `decision_id`. Most attempts
51
+ produce one record with `attempt_sequence=1`. If expiry, revocation, or policy
52
+ state changes while the first allow record is being durably written, CapAuth
53
+ appends a compensating denial with `attempt_sequence=2` and never returns the
54
+ allow. Audit consumers treat the highest sequence as the terminal disposition.
55
+
56
+ ## Application composition
57
+
58
+ ```python
59
+ from capauth.delegated import (
60
+ AuthorizationRequest,
61
+ CapabilityAuthorizer,
62
+ CapabilityScope,
63
+ Principal,
64
+ parse_authorization_bearer,
65
+ )
66
+
67
+ request = AuthorizationRequest(
68
+ principal=Principal(
69
+ principal_id="current-session-id",
70
+ subject="agent@example.test",
71
+ kind="agent",
72
+ ),
73
+ scope=CapabilityScope(
74
+ audience="records-api",
75
+ target="record.read",
76
+ capability="records.read",
77
+ operation="read",
78
+ resource_type="record",
79
+ resource_id="record-7",
80
+ constraints=frozenset({"tenant:blue"}),
81
+ ),
82
+ correlation_id="request-123",
83
+ )
84
+
85
+ presented = parse_authorization_bearer(bearer_value)
86
+ decision = authorizer.authorize(presented, request)
87
+ ```
88
+
89
+ Construct `CapabilityAuthorizer` with production implementations of
90
+ `TrustedIssuerBackend`, `PrincipalPolicyBackend`, `RevocationBackend`,
91
+ `ReplayBackend`, and `AuditSink`. The included in-memory implementations are
92
+ only for tests and isolated local development. They do not coordinate across
93
+ processes or hosts.
94
+
95
+ By default, signature checks use the existing
96
+ `CapAuthSignatureVerifier`, which pins the detached OpenPGP signature to the
97
+ issuer named by the signed payload. Tests may inject a `SignatureVerifier`
98
+ without changing any other policy boundary.
99
+
100
+ ## Operational requirements
101
+
102
+ Production callers should:
103
+
104
+ 1. derive `AuthorizationRequest.principal` from authentication, never request JSON;
105
+ 2. derive the complete `CapabilityScope` from trusted route and resource data;
106
+ 3. use authoritative, fail-closed current-state backends;
107
+ 4. implement replay reservation as one atomic durable operation shared by every worker;
108
+ 5. persist only `AuthorizationDecision`, never a bearer or parsed token;
109
+ 6. discard `PresentedCapability` after the request finishes.
110
+
111
+ The signer interface accepts payload bytes and returns a detached signature.
112
+ Private key custody remains with the configured OpenPGP backend, hardware token,
113
+ or signing sidecar. This module never accepts, serializes, or stores a private key.
@@ -0,0 +1,85 @@
1
+ # Downstream durable backend integration checklist
2
+
3
+ This checklist is for consumers that persist CapAuth authorization state
4
+ (revocations, replay reservations, principal snapshots, issuer policy) in
5
+ their own PostgreSQL database. CapAuth provides the capability token model,
6
+ verification, and contracts. The consumer owns the schema, the roles, the
7
+ grants, and the migrations. Every pitfall below was discovered by a red
8
+ gate in the first reference consumer, not by documentation.
9
+
10
+ ## 1. Provision roles before migrations
11
+
12
+ Migration runner roles are deliberately least-privilege and cannot create
13
+ roles (`NOCREATEROLE`). Any role your migrations `GRANT` to must be created
14
+ by an administrator provisioning step that runs before the migration runner.
15
+
16
+ - Create grantee roles idempotently in a provisioning script, never inside a
17
+ digest-pinned migration.
18
+ - Use the same least-privilege attribute profile as the migrator:
19
+ `LOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE NOINHERIT NOBYPASSRLS
20
+ NOREPLICATION`.
21
+ - Reject role-graph membership and object ownership for runtime roles at
22
+ provision time, and read the profile back after creating it. Fail closed
23
+ on any drift.
24
+ - Document every role class (migrator, per-principal runtime logins, shared
25
+ function grantees) and how they relate. An undocumented grantee inside a
26
+ security migration is a finding, not a detail.
27
+
28
+ ## 2. Re-apply privilege hardening after any function re-creation
29
+
30
+ PostgreSQL grants `EXECUTE` on new functions to `PUBLIC` by default. The
31
+ durable CapAuth functions are `SECURITY DEFINER`, so a missed revoke is a
32
+ real privilege leak, not hygiene.
33
+
34
+ - Every `CREATE FUNCTION` for a CapAuth backend function must be followed,
35
+ in the same migration, by `REVOKE ALL ON FUNCTION <signature> FROM
36
+ PUBLIC` and the explicit `GRANT EXECUTE ... TO <role>`.
37
+ - `DROP FUNCTION` plus `CREATE FUNCTION` discards all previous revokes and
38
+ grants. `CREATE OR REPLACE FUNCTION` preserves them. Prefer `CREATE OR
39
+ REPLACE`; if a signature change forces `DROP` plus `CREATE`, re-apply
40
+ every privilege statement from the original migration.
41
+ - Down migrations that drop and restore a function must restore its
42
+ privilege state as well, or the restored object silently changes the
43
+ security posture.
44
+ - Add a lint rule over your migration set so this class is caught by
45
+ tooling. The reference consumer lints that every definer function carries
46
+ its `REVOKE FROM PUBLIC` and that re-created functions re-apply prior
47
+ grants.
48
+
49
+ ## 3. Verify grants by readback, not by construction
50
+
51
+ Do not trust that the provisioning script produced the intended privilege
52
+ set. Read the effective privileges back and compare against an exact
53
+ allowlist.
54
+
55
+ - `has_function_privilege` and `information_schema.role_table_grants`
56
+ include privileges inherited through `PUBLIC`. That is the point: the
57
+ readback must see what a role can actually do.
58
+ - Fail provisioning when the readback differs from the allowlist in either
59
+ direction (missing or extra privileges).
60
+ - Repeat the role drift check at application startup; RLS cannot contain a
61
+ login once an administrator grants it `BYPASSRLS`, so role governance is a
62
+ standing deployment trust boundary.
63
+
64
+ ## 4. Test against a real database
65
+
66
+ Fake executors cannot observe default `PUBLIC` grants, trigger behavior, or
67
+ RLS interaction. The exact surface that is the production prerequisite is
68
+ the one that must have real-database integration coverage.
69
+
70
+ - Run the full migration set against a disposable PostgreSQL (for example a
71
+ digest-pinned container with tmpfs storage) in CI.
72
+ - Exercise the definer functions through the same provisioning path
73
+ production uses, including concurrent replay-reservation attempts.
74
+ - Verify the complete down/up cycle: after a rollback, functions, triggers,
75
+ and privilege state must match the pre-migration baseline.
76
+
77
+ ## Ownership summary
78
+
79
+ | Artifact | Owner |
80
+ |----------|-------|
81
+ | Token model, verification, capability contracts | CapAuth library |
82
+ | Schema, tables, definer functions, triggers | Consumer migrations |
83
+ | Roles and their least-privilege profiles | Consumer provisioning |
84
+ | Grants and PUBLIC revokes | Consumer migrations, verified by readback |
85
+ | Migration lint and integration qualification | Consumer CI |
@@ -147,3 +147,34 @@ change ID, and verification outcome. Never record passphrases or private-key
147
147
  content. Prohibited shortcuts include agent-generated human keys, reusing an
148
148
  agent key as a person, unsigned votes, copying secrets through chat, accepting
149
149
  a mismatched backup, or deleting old identity history to make checks green.
150
+
151
+ ## 10. Verified 2026-08-20 Chef rotation and follow-up
152
+
153
+ The governed recovery ceremony replaced the unavailable prior Chef signing key
154
+ with fingerprint `ADAD14CCAC8D6D0BF5A4209DB994E78200BF6422`. The prior public
155
+ identity was retained under the owner-only `retired-keys/` history, the live
156
+ registry was updated additively, a fresh public-state backup was restore-tested,
157
+ and `capauth doctor custody` passed. The authenticated authorization for change
158
+ `chg-a543c87b` was consumed once and the canonical ITIL fold reported the change
159
+ approved. The fleet `_freeze.json` and `_protected.json` plane files were then
160
+ signed and verified against the new Chef key; signing did not unfreeze ATLAS.
161
+
162
+ During the plane-signing ceremony, the passphrase was accidentally disclosed in
163
+ terminal/chat history and briefly materialized as a plaintext filename. The file
164
+ was securely removed, but deletion does not make a disclosed secret private
165
+ again. Rotation was explicitly deferred by the owner and is tracked as high
166
+ priority card `da8a6401`. Until that card closes:
167
+
168
+ - never copy the disclosed value into tickets, documentation, commands, or new
169
+ configuration;
170
+ - use hidden interactive input only when a governed signing ceremony is
171
+ unavoidable;
172
+ - do not claim the passphrase is uncompromised; and
173
+ - preserve the same fingerprint during passphrase-only re-protection unless a
174
+ full key rotation is independently required.
175
+
176
+ The command anti-pattern was `echo SECRET > $CAPAUTH_PASSPHRASE`: shell
177
+ redirection treats the expanded secret as a path, while the signer expects the
178
+ environment variable itself to contain the secret. The supported pattern is a
179
+ hidden prompt followed by a short-lived exported variable, immediate `unset`,
180
+ and no shell tracing. No secret literal belongs in this runbook.
@@ -0,0 +1,128 @@
1
+ # Identity estate discovery and retirement
2
+
3
+ `capauth doctor custody` validates one configured identity. `capauth doctor
4
+ estate` validates the wider machine estate: canonical and legacy CapAuth roots,
5
+ SKCapstone agent/named-identity roots, alternate user homes, GnuPG keyrings, and
6
+ Syncthing folders which contain those roots.
7
+
8
+ The workflow is read-only. It never removes a key and never emits private key or
9
+ passphrase bytes. An optional evidence file contains paths, primary
10
+ fingerprints, manifest digest, classifications, and results.
11
+
12
+ ## Authoritative manifest
13
+
14
+ Keep `estate.json` in the CapAuth home or pass it explicitly with `--manifest`.
15
+ Version 1 distinguishes human, service, and node identities, records lifecycle
16
+ state, constrains active private-key placement, and links retired fingerprints
17
+ to verified encrypted quarantine:
18
+
19
+ ```json
20
+ {
21
+ "version": 1,
22
+ "identities": [
23
+ {
24
+ "fingerprint": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
25
+ "status": "active",
26
+ "identity_type": "human",
27
+ "label": "operator",
28
+ "allowed_secret_roots": [
29
+ "/home/operator/.skcapstone/identities/operator/capauth"
30
+ ]
31
+ },
32
+ {
33
+ "fingerprint": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
34
+ "status": "retired",
35
+ "identity_type": "service",
36
+ "label": "former service signer",
37
+ "quarantine": {
38
+ "archive": "/custody/capauth/retired-service.tar.gpg",
39
+ "sha256": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
40
+ "verified_at": "2026-08-21T00:00:00Z",
41
+ "verified_by": "operator"
42
+ }
43
+ }
44
+ ]
45
+ }
46
+ ```
47
+
48
+ Fingerprints must be full 40- or 64-hex OpenPGP primary fingerprints.
49
+ `identity_type` is `human`, `service`, or `node`; `status` is `active` or
50
+ `retired`. Paths may be absolute or relative to the manifest.
51
+
52
+ For retired identities, `archive` must exist and end in `.gpg`, and its current
53
+ SHA-256 must match the manifest. `verified_at` and `verified_by` are operator
54
+ attestations that the encrypted archive was decrypt-tested and its expected
55
+ members/fingerprints were checked. The hash check does not replace that
56
+ decryptability test.
57
+
58
+ ## Audit
59
+
60
+ Run against the current user:
61
+
62
+ ```bash
63
+ capauth doctor estate \
64
+ --manifest ~/.skcapstone/capauth/estate.json \
65
+ --evidence /var/tmp/capauth-estate-evidence.json
66
+ ```
67
+
68
+ Name every alternate home. This prevents a different login home from escaping
69
+ the inventory:
70
+
71
+ ```bash
72
+ capauth doctor estate \
73
+ --manifest /path/to/estate.json \
74
+ --user-home /home/operator \
75
+ --user-home /home/alternate
76
+ ```
77
+
78
+ Use repeatable `--root` for identities outside the known layouts and
79
+ `--syncthing-config` for nonstandard Syncthing configuration paths. GnuPG
80
+ keyring inspection is on by default; `--no-gpg` exists for hermetic tests.
81
+
82
+ The command exits nonzero for:
83
+
84
+ - physical legacy and canonical trees which diverge;
85
+ - private keys outside their declared custody roots;
86
+ - unmanifested private keys;
87
+ - any discovered retired key copy;
88
+ - Syncthing conflict copies;
89
+ - missing private-key or root-revocation ignore rules at the actual Syncthing
90
+ folder root;
91
+ - absent, non-encrypted, or hash-mismatched quarantine evidence.
92
+
93
+ An unmanifested public key and multiple copies of the same active secret key
94
+ warn. Public keys classified as active and correctly placed active private keys
95
+ pass.
96
+
97
+ ## Retirement gate
98
+
99
+ 1. Record the fingerprint as `retired`; do not delete anything.
100
+ 2. Stream the complete material into an encrypted `.gpg` quarantine archive.
101
+ 3. Decrypt-test the archive in a temporary location and verify expected
102
+ fingerprints/members.
103
+ 4. Record the archive SHA-256 and the operator attestation in the manifest.
104
+ 5. Run `capauth doctor estate --evidence ...`. The quarantine check must be
105
+ `OK`; discovered retired copies will still be `FAIL`.
106
+ 6. Under an approved change, remove only the exact reported copies and GnuPG
107
+ primary fingerprints.
108
+ 7. Rerun the audit. Retired-copy findings must be gone while quarantine remains
109
+ `OK`. Attach the evidence file and manifest digest to the change record.
110
+
111
+ This two-run pattern makes removal contingent on verified, encrypted,
112
+ recoverable custody and produces evidence without automating destructive key
113
+ deletion.
114
+
115
+ ## Syncthing boundary
116
+
117
+ `capauth sync` now writes required `.stignore` rules before registering a
118
+ folder and fails closed if it cannot do so. Running it against an already
119
+ configured identity root also repairs missing required exclusions. At minimum,
120
+ the folder root must contain:
121
+
122
+ ```text
123
+ **/private.*
124
+ **/root-revocation.asc
125
+ ```
126
+
127
+ The setup command also excludes common passphrase, environment, SQLite, and
128
+ backup paths. Treat synced state as public distribution, never custody.
@@ -32,6 +32,7 @@ dependencies = [
32
32
  "pyyaml>=6.0",
33
33
  "rich>=13.0",
34
34
  "httpx>=0.25",
35
+ "typing-extensions>=4.8",
35
36
  ]
36
37
 
37
38
  [project.optional-dependencies]
@@ -71,20 +71,30 @@ DEFAULT_AGENTS = [
71
71
  # ---------------------------------------------------------------------------
72
72
 
73
73
 
74
- def _load_cluster() -> dict:
75
- """Return cluster.json contents or a safe default."""
74
+ def _load_cluster() -> dict | None:
75
+ """Return cluster.json contents, or None when absent/unreadable.
76
+
77
+ No fabricated default: inventing an operator/realm here once wrote
78
+ ``<agent>@chef.skworld`` FQIDs into identity.json on hosts that were
79
+ never part of that cluster. Callers must tolerate None (fqid omitted).
80
+ """
76
81
  for p in [Path("/etc/skcapstone/cluster.json"), SKCAPSTONE_HOME / "cluster.json"]:
77
82
  if p.exists():
78
83
  try:
79
84
  return json.loads(p.read_text(encoding="utf-8"))
80
85
  except Exception:
81
86
  pass
82
- return {"realm": "skworld", "operator": "chef"}
87
+ return None
83
88
 
84
89
 
85
- def _build_fqid(agent: str, cluster: dict) -> str:
86
- realm = cluster.get("realm", "skworld")
87
- operator = cluster.get("operator", "chef")
90
+ def _build_fqid(agent: str, cluster: dict | None) -> str | None:
91
+ """Build ``<agent>@<operator>.<realm>``, or None without cluster data."""
92
+ if not cluster:
93
+ return None
94
+ realm = cluster.get("realm")
95
+ operator = cluster.get("operator")
96
+ if not realm or not operator:
97
+ return None
88
98
  return f"{agent}@{operator}.{realm}"
89
99
 
90
100
 
@@ -129,11 +139,16 @@ def _update_identity_json(
129
139
  identity_path: Path,
130
140
  agent: str,
131
141
  capauth_uri: str,
132
- fqid: str,
142
+ fqid: str | None,
133
143
  fingerprint: str | None,
134
144
  dry_run: bool,
135
145
  ) -> None:
136
- """Write or merge dual-URI fields into identity.json."""
146
+ """Write or merge dual-URI fields into identity.json.
147
+
148
+ ``fqid`` is written only when resolvable from cluster.json; when the
149
+ cluster is unknown it is left untouched rather than overwritten with a
150
+ fabricated or null value.
151
+ """
137
152
  existing: dict = {}
138
153
  if identity_path.exists():
139
154
  try:
@@ -144,7 +159,8 @@ def _update_identity_json(
144
159
  updated = dict(existing)
145
160
  # Always set these canonical fields
146
161
  updated["capauth_uri"] = capauth_uri
147
- updated["fqid"] = fqid
162
+ if fqid is not None:
163
+ updated["fqid"] = fqid
148
164
  if fingerprint:
149
165
  updated["fingerprint"] = fingerprint
150
166
  # Ensure core fields are present