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.
- {capauth-0.3.4 → capauth-0.3.6}/CHANGELOG.md +26 -0
- {capauth-0.3.4 → capauth-0.3.6}/PKG-INFO +11 -4
- {capauth-0.3.4 → capauth-0.3.6}/README.md +9 -3
- {capauth-0.3.4 → capauth-0.3.6}/SOP.md +5 -0
- {capauth-0.3.4 → capauth-0.3.6}/docs/ARCHITECTURE.md +3 -2
- {capauth-0.3.4 → capauth-0.3.6}/docs/COLD_MACHINE_BOOTSTRAP_AND_DR.md +8 -7
- capauth-0.3.6/docs/DELEGATED_CAPABILITIES.md +113 -0
- capauth-0.3.6/docs/DOWNSTREAM_DURABLE_BACKENDS.md +85 -0
- {capauth-0.3.4 → capauth-0.3.6}/docs/HUMAN_IDENTITY_SETUP_AND_ROTATION.md +31 -0
- capauth-0.3.6/docs/IDENTITY_ESTATE.md +128 -0
- {capauth-0.3.4 → capauth-0.3.6}/pyproject.toml +1 -0
- {capauth-0.3.4 → capauth-0.3.6}/scripts/provision_agent_profiles.py +25 -9
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/__init__.py +69 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/cli.py +144 -19
- capauth-0.3.6/src/capauth/delegated.py +1489 -0
- capauth-0.3.6/src/capauth/estate.py +894 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/sync.py +70 -6
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth.egg-info/PKG-INFO +11 -4
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth.egg-info/SOURCES.txt +8 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth.egg-info/requires.txt +1 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_agent_identity.py +57 -22
- capauth-0.3.6/tests/test_delegated_capabilities.py +566 -0
- capauth-0.3.6/tests/test_estate.py +300 -0
- capauth-0.3.6/tests/test_sync.py +48 -0
- {capauth-0.3.4 → capauth-0.3.6}/.cursorrules +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/.dockerignore +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/.github/workflows/ci.yml +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/.github/workflows/docs-check.yml +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/.github/workflows/nextcloud-appstore.yml +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/.github/workflows/publish.yml +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/.github/workflows/pytest.yml +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/.github/workflows/secret-scan.yml +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/.gitignore +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/.gitleaks-baseline.json +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/AGENTS.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/AI-ADVOCATE.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/ARCHITECTURE.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/CLAUDE.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/CODE_OF_CONDUCT.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/CONTRIBUTING.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/Dockerfile +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/Dockerfile.authentik-capauth +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/LICENSE +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/MANIFEST.in +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/MISSION.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/SECURITY.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/SKILL.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/authentik-custom/capauth_migrate.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/authentik-custom/imghdr.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/authentik-custom/user_settings.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/bin/cli.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/.gitignore +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/README.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/RELEASING.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/background.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/content_scripts/autofill.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/content_scripts/cross_origin_signing.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/content_scripts/detector.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/content_scripts/provider.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/content_scripts/provider_bridge.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/icons/icon.svg +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/icons/icon128.png +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/icons/icon16.png +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/icons/icon48.png +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/lib/bunker-e2e.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/lib/keyqr.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/lib/keyvault.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/lib/openpgp.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/lib/signer-backends.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/manifest.firefox.json +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/manifest.json +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/native-host/capauth_signer.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/native-host/com.capauth.signer.json +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/native-host/install.sh +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/options/options.html +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/options/options.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/package-lock.json +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/package.json +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/popup/popup.css +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/popup/popup.html +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/popup/popup.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/scripts/build.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/scripts/generate-icons.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/stages/AuthenticatorSelectionStage.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/stages/CapAuthStage.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/stages/QRDisplayStage.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/stages/SignatureVerificationStage.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/stages/StagePipeline.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/submission/STORE_LISTING.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/submission/privacy-policy.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/tests/fixtures/test_key.json +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/tests/unit/background.test.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/tests/unit/bunker_e2e.test.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/tests/unit/detector.test.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/tests/unit/keyqr.test.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/tests/unit/keyvault.test.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/tests/unit/openpgp.test.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/tests/unit/provider_api.test.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/tests/unit/provider_tierb.test.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/tests/unit/remote_bunker.test.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/tests/unit/signer_backends.test.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/browser-extension/vitest.config.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/deploy/capauth-service/.env.example +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/deploy/capauth-service/deploy.sh +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/deploy/capauth-service/docker-compose.yml +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/deploy/capauth-service/systemd/capauth-backup.service +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/deploy/capauth-service/systemd/capauth-backup.timer +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/deploy/forgejo-capauth/.env.example +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/deploy/forgejo-capauth/docker-compose.yml +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/deploy/forgejo-capauth/preflight.sh +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/docs/AIRGAPPED_SIGNING_WORKSTATION.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/docs/AUTHENTIK_CUSTOM_STAGE.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/docs/AUTHENTIK_DEPLOYMENT_SKSSO.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/docs/AUTHENTIK_FORGEJO_DEPLOYMENT.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/docs/AUTHENTIK_STRATEGY_DECISION.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/docs/CAPAUTH_BUNKER_REMOTE_SIGNER.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/docs/CAPAUTH_OIDC_IDP.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/docs/CLAIMS.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/docs/CRYPTO_SPEC.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/docs/ENTERPRISE_MANAGED_KEYS.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/docs/INTEGRATION_BLUEPRINT.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/docs/M0-capauth-api-v1.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/docs/M0-capauth-importer-inventory.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/docs/PQC_CAPAUTH_REVISIT.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/docs/PQC_ROOT_MIGRATION.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/docs/PROTOCOL.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/docs/ROOT_ROTATION_CEREMONY.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/docs/TRUST_GRAPH_COORD_PROJECTION.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/docs/ZERO_KNOWLEDGE_PROFILE.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/docs/authentik-capauth.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/docs/deploy-plan/capauth-bulletproof-deploy.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/index.d.ts +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/index.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/openclaw-plugin.archived-2026-04-23/package.json +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/openclaw-plugin.archived-2026-04-23/src/index.ts +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/openclaw-plugin.archived-2026-04-23/src/openclaw.plugin.json +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/package.json +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/phone-signer/app.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/phone-signer/icons/icon-192.png +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/phone-signer/icons/icon-512.png +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/phone-signer/index.html +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/phone-signer/lib/bunker-e2e.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/phone-signer/lib/bunker-signer.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/phone-signer/lib/canonical.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/phone-signer/lib/keyqr.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/phone-signer/lib/keyvault.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/phone-signer/manifest.webmanifest +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/phone-signer/sw.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/phone-signer/vendor/openpgp.min.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/phone-signer/vendor/qrcode-generator.js +0 -0
- {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
- {capauth-0.3.4 → capauth-0.3.6}/scripts/build_authentik_capauth_image.sh +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/scripts/capauth-backup.sh +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/scripts/capauth-restore.sh +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/scripts/migrate_canonical_subjects.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/scripts/pqc_ceremony_dryrun.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/setup.cfg +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/skill.yaml +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/agent_identity.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/apps.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/authentik/__init__.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/authentik/api.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/authentik/blueprints/capauth-pgp-login.yaml +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/authentik/claims_mapper.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/authentik/nonce_store.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/authentik/stage.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/authentik/urls.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/authentik/verifier.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/authz.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/crypto/__init__.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/crypto/base.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/crypto/gnupg_backend.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/crypto/pgpy_backend.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/crypto/sequoia_backend.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/crypto/skpgp_backend.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/custody.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/did.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/discovery/__init__.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/discovery/base.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/discovery/file_discovery.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/discovery/mdns.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/discovery/syncthing.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/exceptions.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/identity.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/identity_class.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integration.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/__init__.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/forgejo/__init__.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/forgejo/auth_flow.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/forgejo/cli.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/forgejo/config.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/forgejo/forgejo_api.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/forgejo/oidc_provider.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/forgejo/tests/__init__.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/forgejo/tests/test_auth_flow.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/forgejo/tests/test_cli.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/forgejo/tests/test_config.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/forgejo/tests/test_forgejo_api.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/forgejo/tests/test_oidc_provider.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/.gitignore +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/.nextcloudignore +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/CSR-PR.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/INFO-XML-CHANGES.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/PUBLISHING.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/README.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/appinfo/info.xml +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/appinfo/routes.php +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/build-release.sh +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/certificates/.gitignore +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/certificates/capauth.crt +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/certificates/capauth.csr +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/composer.json +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/css/login.css +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/js/login.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/krankerl.toml +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/AlternativeLogin/CapAuthLogin.php +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/AppInfo/Application.php +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/BackgroundJob/PruneExpiredNonces.php +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/Controller/LoginController.php +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/Controller/TokenController.php +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/Db/KeyRegistry.php +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/Exception/CapAuthUnauthorizedException.php +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/Middleware/PgpVerificationMiddleware.php +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/Migration/Version000300Date20260301000000.php +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/Provider/CapAuthProvider.php +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/Service/ChallengeService.php +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/Service/GroupSyncService.php +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/Service/UserProvisioningService.php +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/Service/VerifierService.php +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/lib/User/Backend.php +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/phpunit.xml +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/screenshots/README.md +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/screenshots/login.png +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/templates/login.php +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/tests/bootstrap.php +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/tests/integration/FullAuthFlowTest.php +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/tests/oidc/oidc_e2e_login_test.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/tests/unit/Controller/LoginControllerTest.php +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/tests/unit/Db/KeyRegistryTest.php +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/tests/unit/Provider/CapAuthProviderTest.php +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/tests/unit/Service/ChallengeServiceTest.php +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/tests/unit/Service/VerifierServiceTest.php +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/integrations/nextcloud/tests/unit/User/BackendTest.php +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/key_transparency.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/login.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/manifest.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/mesh.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/migrations/0001_initial.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/migrations/0002_widen_fingerprint_to_64.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/migrations/__init__.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/models.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pairing/__init__.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pairing/canonicalize.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pairing/kernel.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pairing/operator_session.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pairing/proof.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pairing/records.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pairing/store.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pairing/window.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pma.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pqc_confidentiality.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pqc_identity.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pqc_root_identity.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/pqc_tokens.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/profile.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/provisioning.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/registry.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/seal.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/__init__.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/app.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/bunker.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/bunker_e2e.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/keystore.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/oidc/__init__.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/oidc/clients.example.json +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/oidc/clients.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/oidc/passkey.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/oidc/provider.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/oidc/signing_key.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/oidc/store.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/push.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/service/server.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/subject.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/testing.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/tokens.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/trust/__init__.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/trust/calibration.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth/trust/graph.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth.egg-info/dependency_links.txt +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth.egg-info/entry_points.txt +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/src/capauth.egg-info/top_level.txt +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/stages/ak-stage-capauth/.gitignore +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/stages/ak-stage-capauth/package-lock.json +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/stages/ak-stage-capauth/package.json +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/stages/ak-stage-capauth/src/CapAuthStage.test.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/stages/ak-stage-capauth/src/CapAuthStage.vue +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/stages/ak-stage-capauth/src/index.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/stages/ak-stage-capauth/vite.config.js +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/__init__.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/conftest.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/fixtures/bunker_e2e_v1_vector.json +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/fixtures/canonical_nonce_v2_vector.json +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_agentrun_capabilities.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_audience_mint_helper.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_audience_tokens.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_authz.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_authz_legacy_grace.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_authz_signature_gate.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_backup_restore.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_bunker.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_bunker_e2e.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_canonicalize.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_capauth_service_deploy.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_change_capabilities.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_claims_mapper.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_crypto.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_custody.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_device_key_proof.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_did_capability_relationships.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_enrollment_proof_helper.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_fingerprint_v6.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_forgejo_preflight.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_home_resolution.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_identity.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_identity_class.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_identity_ttl.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_integration.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_key_revocation_expiry.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_key_transparency.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_login.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_manifest.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_mesh.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_models_pqc_q0.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_nextcloud_app.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_no_subapp_dependency.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_nonce_store.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_oidc_idp.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_oidc_idp_e2e.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_operator_session.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_pairing.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_pairing_base_dir_env.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_passkey.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_pma.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_pqc_confidentiality.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_pqc_identity.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_pqc_root_integration.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_pqc_t3_gate.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_pqc_tokens.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_profile.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_provisioning.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_push.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_py313_compat.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_qr_login.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_real_pgp_e2e.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_register_cli.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_registry.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_seal.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_sequoia_backend.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_service.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_service_authz_decide.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_service_v6_fingerprint.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_skpgp_backend.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_subject.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_syncthing_discovery.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_testing_helper.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_token_gc.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_token_store_flag.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_tokens.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_trust_calibration.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_trust_graph.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_trust_graph_coord_audit.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_verifier.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_version_honesty.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tests/test_zk_profile.py +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/tools/build-sq.sh +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/urllib.error +0 -0
- {capauth-0.3.4 → capauth-0.3.6}/web/README.md +0 -0
- {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.
|
|
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
|
|
186
|
-
|
|
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 #
|
|
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
|
|
124
|
-
|
|
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 #
|
|
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["
|
|
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
|
|
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
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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
|
-
|
|
260
|
-
|
|
261
|
-
generates a *new* keypair and
|
|
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.
|
|
@@ -71,20 +71,30 @@ DEFAULT_AGENTS = [
|
|
|
71
71
|
# ---------------------------------------------------------------------------
|
|
72
72
|
|
|
73
73
|
|
|
74
|
-
def _load_cluster() -> dict:
|
|
75
|
-
"""Return cluster.json contents or
|
|
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
|
|
87
|
+
return None
|
|
83
88
|
|
|
84
89
|
|
|
85
|
-
def _build_fqid(agent: str, cluster: dict) -> str:
|
|
86
|
-
realm
|
|
87
|
-
|
|
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
|
-
|
|
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
|