pyscoped 0.1.0__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.
- pyscoped-0.1.0/.gitignore +34 -0
- pyscoped-0.1.0/CLAUDE.md +395 -0
- pyscoped-0.1.0/LICENSE +21 -0
- pyscoped-0.1.0/PKG-INFO +248 -0
- pyscoped-0.1.0/README.md +213 -0
- pyscoped-0.1.0/docs/architecture.md +236 -0
- pyscoped-0.1.0/docs/extensions/A1-migrations.md +70 -0
- pyscoped-0.1.0/docs/extensions/A2-contracts.md +124 -0
- pyscoped-0.1.0/docs/extensions/A3-rule-extensions.md +118 -0
- pyscoped-0.1.0/docs/extensions/A4-blobs.md +106 -0
- pyscoped-0.1.0/docs/extensions/A5-config.md +96 -0
- pyscoped-0.1.0/docs/extensions/A6-search.md +91 -0
- pyscoped-0.1.0/docs/extensions/A7-templates.md +119 -0
- pyscoped-0.1.0/docs/extensions/A8-tiering.md +175 -0
- pyscoped-0.1.0/docs/extensions/A9-import-export.md +131 -0
- pyscoped-0.1.0/docs/layers/00-compliance.md +203 -0
- pyscoped-0.1.0/docs/layers/01-registry.md +165 -0
- pyscoped-0.1.0/docs/layers/02-identity.md +122 -0
- pyscoped-0.1.0/docs/layers/03-objects.md +150 -0
- pyscoped-0.1.0/docs/layers/04-tenancy.md +148 -0
- pyscoped-0.1.0/docs/layers/05-rules.md +188 -0
- pyscoped-0.1.0/docs/layers/06-audit.md +139 -0
- pyscoped-0.1.0/docs/layers/07-temporal.md +90 -0
- pyscoped-0.1.0/docs/layers/08-environments.md +174 -0
- pyscoped-0.1.0/docs/layers/09-flow.md +193 -0
- pyscoped-0.1.0/docs/layers/10-deployments.md +138 -0
- pyscoped-0.1.0/docs/layers/11-secrets.md +212 -0
- pyscoped-0.1.0/docs/layers/12-integrations.md +216 -0
- pyscoped-0.1.0/docs/layers/13-connector.md +273 -0
- pyscoped-0.1.0/docs/layers/14-events.md +183 -0
- pyscoped-0.1.0/docs/layers/15-notifications.md +173 -0
- pyscoped-0.1.0/docs/layers/16-scheduling.md +165 -0
- pyscoped-0.1.0/examples/django_project/.gitignore +4 -0
- pyscoped-0.1.0/examples/django_project/README.md +188 -0
- pyscoped-0.1.0/examples/django_project/core/__init__.py +0 -0
- pyscoped-0.1.0/examples/django_project/core/context_processors.py +44 -0
- pyscoped-0.1.0/examples/django_project/core/management/__init__.py +0 -0
- pyscoped-0.1.0/examples/django_project/core/management/commands/__init__.py +0 -0
- pyscoped-0.1.0/examples/django_project/core/management/commands/seed_demo.py +464 -0
- pyscoped-0.1.0/examples/django_project/core/middleware.py +29 -0
- pyscoped-0.1.0/examples/django_project/core/migrations/0001_initial.py +28 -0
- pyscoped-0.1.0/examples/django_project/core/migrations/__init__.py +0 -0
- pyscoped-0.1.0/examples/django_project/core/models.py +25 -0
- pyscoped-0.1.0/examples/django_project/core/services.py +151 -0
- pyscoped-0.1.0/examples/django_project/core/templatetags/__init__.py +0 -0
- pyscoped-0.1.0/examples/django_project/core/templatetags/scoped_tags.py +189 -0
- pyscoped-0.1.0/examples/django_project/core/views.py +18 -0
- pyscoped-0.1.0/examples/django_project/db.sqlite3 +0 -0
- pyscoped-0.1.0/examples/django_project/launchpad/__init__.py +0 -0
- pyscoped-0.1.0/examples/django_project/launchpad/settings.py +96 -0
- pyscoped-0.1.0/examples/django_project/launchpad/urls.py +15 -0
- pyscoped-0.1.0/examples/django_project/launchpad/wsgi.py +7 -0
- pyscoped-0.1.0/examples/django_project/manage.py +21 -0
- pyscoped-0.1.0/examples/django_project/requirements.txt +3 -0
- pyscoped-0.1.0/examples/django_project/static/css/custom.css +53 -0
- pyscoped-0.1.0/examples/django_project/static/js/app.js +38 -0
- pyscoped-0.1.0/examples/django_project/templates/audit/detail.html +110 -0
- pyscoped-0.1.0/examples/django_project/templates/audit/list.html +90 -0
- pyscoped-0.1.0/examples/django_project/templates/base.html +67 -0
- pyscoped-0.1.0/examples/django_project/templates/components/_data_table.html +15 -0
- pyscoped-0.1.0/examples/django_project/templates/components/_empty_state.html +11 -0
- pyscoped-0.1.0/examples/django_project/templates/components/_header.html +19 -0
- pyscoped-0.1.0/examples/django_project/templates/components/_modal.html +13 -0
- pyscoped-0.1.0/examples/django_project/templates/components/_pagination.html +16 -0
- pyscoped-0.1.0/examples/django_project/templates/components/_sidebar.html +147 -0
- pyscoped-0.1.0/examples/django_project/templates/components/_stat_card.html +9 -0
- pyscoped-0.1.0/examples/django_project/templates/components/_state_badge.html +13 -0
- pyscoped-0.1.0/examples/django_project/templates/components/_tabs.html +13 -0
- pyscoped-0.1.0/examples/django_project/templates/components/_timeline.html +31 -0
- pyscoped-0.1.0/examples/django_project/templates/components/_toast.html +7 -0
- pyscoped-0.1.0/examples/django_project/templates/components/_user_switcher.html +26 -0
- pyscoped-0.1.0/examples/django_project/templates/config/list.html +105 -0
- pyscoped-0.1.0/examples/django_project/templates/connectors/list.html +64 -0
- pyscoped-0.1.0/examples/django_project/templates/dashboard/index.html +67 -0
- pyscoped-0.1.0/examples/django_project/templates/deployments/detail.html +70 -0
- pyscoped-0.1.0/examples/django_project/templates/deployments/list.html +97 -0
- pyscoped-0.1.0/examples/django_project/templates/environments/detail.html +109 -0
- pyscoped-0.1.0/examples/django_project/templates/environments/list.html +100 -0
- pyscoped-0.1.0/examples/django_project/templates/events/list.html +62 -0
- pyscoped-0.1.0/examples/django_project/templates/flags/list.html +60 -0
- pyscoped-0.1.0/examples/django_project/templates/health/index.html +147 -0
- pyscoped-0.1.0/examples/django_project/templates/integrations/list.html +95 -0
- pyscoped-0.1.0/examples/django_project/templates/notifications/list.html +69 -0
- pyscoped-0.1.0/examples/django_project/templates/pipelines/list.html +72 -0
- pyscoped-0.1.0/examples/django_project/templates/registration/login.html +72 -0
- pyscoped-0.1.0/examples/django_project/templates/rules/list.html +89 -0
- pyscoped-0.1.0/examples/django_project/templates/scheduling/list.html +153 -0
- pyscoped-0.1.0/examples/django_project/templates/secrets/list.html +112 -0
- pyscoped-0.1.0/examples/django_project/templates/services/create.html +53 -0
- pyscoped-0.1.0/examples/django_project/templates/services/detail.html +144 -0
- pyscoped-0.1.0/examples/django_project/templates/services/list.html +62 -0
- pyscoped-0.1.0/examples/django_project/templates/services/search.html +83 -0
- pyscoped-0.1.0/examples/django_project/templates/teams/detail.html +101 -0
- pyscoped-0.1.0/examples/django_project/templates/teams/list.html +31 -0
- pyscoped-0.1.0/examples/django_project/templates/templates_page/list.html +57 -0
- pyscoped-0.1.0/examples/django_project/webapp/__init__.py +0 -0
- pyscoped-0.1.0/examples/django_project/webapp/forms.py +1 -0
- pyscoped-0.1.0/examples/django_project/webapp/urls.py +97 -0
- pyscoped-0.1.0/examples/django_project/webapp/views/__init__.py +0 -0
- pyscoped-0.1.0/examples/django_project/webapp/views/audit.py +120 -0
- pyscoped-0.1.0/examples/django_project/webapp/views/config.py +111 -0
- pyscoped-0.1.0/examples/django_project/webapp/views/connectors.py +37 -0
- pyscoped-0.1.0/examples/django_project/webapp/views/dashboard.py +68 -0
- pyscoped-0.1.0/examples/django_project/webapp/views/deployments.py +76 -0
- pyscoped-0.1.0/examples/django_project/webapp/views/environments.py +97 -0
- pyscoped-0.1.0/examples/django_project/webapp/views/events.py +36 -0
- pyscoped-0.1.0/examples/django_project/webapp/views/flags.py +56 -0
- pyscoped-0.1.0/examples/django_project/webapp/views/health.py +85 -0
- pyscoped-0.1.0/examples/django_project/webapp/views/integrations.py +58 -0
- pyscoped-0.1.0/examples/django_project/webapp/views/notifications.py +67 -0
- pyscoped-0.1.0/examples/django_project/webapp/views/pipelines.py +40 -0
- pyscoped-0.1.0/examples/django_project/webapp/views/rules.py +49 -0
- pyscoped-0.1.0/examples/django_project/webapp/views/scheduling.py +62 -0
- pyscoped-0.1.0/examples/django_project/webapp/views/search.py +57 -0
- pyscoped-0.1.0/examples/django_project/webapp/views/secrets.py +84 -0
- pyscoped-0.1.0/examples/django_project/webapp/views/services.py +183 -0
- pyscoped-0.1.0/examples/django_project/webapp/views/teams.py +95 -0
- pyscoped-0.1.0/examples/django_project/webapp/views/templates.py +86 -0
- pyscoped-0.1.0/pyproject.toml +50 -0
- pyscoped-0.1.0/scoped/__init__.py +3 -0
- pyscoped-0.1.0/scoped/audit/__init__.py +17 -0
- pyscoped-0.1.0/scoped/audit/models.py +88 -0
- pyscoped-0.1.0/scoped/audit/query.py +287 -0
- pyscoped-0.1.0/scoped/audit/writer.py +227 -0
- pyscoped-0.1.0/scoped/conf.py +54 -0
- pyscoped-0.1.0/scoped/connector/__init__.py +42 -0
- pyscoped-0.1.0/scoped/connector/bridge.py +452 -0
- pyscoped-0.1.0/scoped/connector/marketplace/__init__.py +21 -0
- pyscoped-0.1.0/scoped/connector/marketplace/discovery.py +196 -0
- pyscoped-0.1.0/scoped/connector/marketplace/models.py +151 -0
- pyscoped-0.1.0/scoped/connector/marketplace/publishing.py +236 -0
- pyscoped-0.1.0/scoped/connector/models.py +210 -0
- pyscoped-0.1.0/scoped/connector/protocol.py +192 -0
- pyscoped-0.1.0/scoped/contrib/__init__.py +1 -0
- pyscoped-0.1.0/scoped/contrib/_base.py +45 -0
- pyscoped-0.1.0/scoped/contrib/django/__init__.py +47 -0
- pyscoped-0.1.0/scoped/contrib/django/apps.py +22 -0
- pyscoped-0.1.0/scoped/contrib/django/backend.py +189 -0
- pyscoped-0.1.0/scoped/contrib/django/management/__init__.py +0 -0
- pyscoped-0.1.0/scoped/contrib/django/management/commands/__init__.py +0 -0
- pyscoped-0.1.0/scoped/contrib/django/management/commands/scoped_audit.py +46 -0
- pyscoped-0.1.0/scoped/contrib/django/management/commands/scoped_compliance.py +34 -0
- pyscoped-0.1.0/scoped/contrib/django/management/commands/scoped_health.py +25 -0
- pyscoped-0.1.0/scoped/contrib/django/middleware.py +90 -0
- pyscoped-0.1.0/scoped/contrib/fastapi/__init__.py +50 -0
- pyscoped-0.1.0/scoped/contrib/fastapi/dependencies.py +36 -0
- pyscoped-0.1.0/scoped/contrib/fastapi/middleware.py +75 -0
- pyscoped-0.1.0/scoped/contrib/fastapi/router.py +47 -0
- pyscoped-0.1.0/scoped/contrib/fastapi/schemas.py +101 -0
- pyscoped-0.1.0/scoped/contrib/flask/__init__.py +29 -0
- pyscoped-0.1.0/scoped/contrib/flask/admin.py +55 -0
- pyscoped-0.1.0/scoped/contrib/flask/extension.py +101 -0
- pyscoped-0.1.0/scoped/contrib/mcp/__init__.py +26 -0
- pyscoped-0.1.0/scoped/contrib/mcp/resources.py +58 -0
- pyscoped-0.1.0/scoped/contrib/mcp/server.py +35 -0
- pyscoped-0.1.0/scoped/contrib/mcp/tools.py +102 -0
- pyscoped-0.1.0/scoped/deployments/__init__.py +29 -0
- pyscoped-0.1.0/scoped/deployments/executor.py +294 -0
- pyscoped-0.1.0/scoped/deployments/gates.py +113 -0
- pyscoped-0.1.0/scoped/deployments/models.py +171 -0
- pyscoped-0.1.0/scoped/deployments/rollback.py +107 -0
- pyscoped-0.1.0/scoped/environments/__init__.py +29 -0
- pyscoped-0.1.0/scoped/environments/container.py +174 -0
- pyscoped-0.1.0/scoped/environments/lifecycle.py +365 -0
- pyscoped-0.1.0/scoped/environments/models.py +234 -0
- pyscoped-0.1.0/scoped/environments/snapshot.py +148 -0
- pyscoped-0.1.0/scoped/events/__init__.py +36 -0
- pyscoped-0.1.0/scoped/events/bus.py +229 -0
- pyscoped-0.1.0/scoped/events/models.py +223 -0
- pyscoped-0.1.0/scoped/events/subscriptions.py +196 -0
- pyscoped-0.1.0/scoped/events/webhooks.py +214 -0
- pyscoped-0.1.0/scoped/exceptions.py +376 -0
- pyscoped-0.1.0/scoped/flow/__init__.py +30 -0
- pyscoped-0.1.0/scoped/flow/engine.py +217 -0
- pyscoped-0.1.0/scoped/flow/models.py +221 -0
- pyscoped-0.1.0/scoped/flow/pipeline.py +264 -0
- pyscoped-0.1.0/scoped/flow/promotion.py +152 -0
- pyscoped-0.1.0/scoped/identity/__init__.py +22 -0
- pyscoped-0.1.0/scoped/identity/context.py +93 -0
- pyscoped-0.1.0/scoped/identity/principal.py +357 -0
- pyscoped-0.1.0/scoped/identity/resolver.py +223 -0
- pyscoped-0.1.0/scoped/integrations/__init__.py +35 -0
- pyscoped-0.1.0/scoped/integrations/connectors.py +168 -0
- pyscoped-0.1.0/scoped/integrations/hooks.py +275 -0
- pyscoped-0.1.0/scoped/integrations/lifecycle.py +291 -0
- pyscoped-0.1.0/scoped/integrations/models.py +233 -0
- pyscoped-0.1.0/scoped/integrations/sandbox.py +141 -0
- pyscoped-0.1.0/scoped/notifications/__init__.py +34 -0
- pyscoped-0.1.0/scoped/notifications/delivery.py +75 -0
- pyscoped-0.1.0/scoped/notifications/engine.py +278 -0
- pyscoped-0.1.0/scoped/notifications/models.py +164 -0
- pyscoped-0.1.0/scoped/notifications/preferences.py +118 -0
- pyscoped-0.1.0/scoped/objects/__init__.py +34 -0
- pyscoped-0.1.0/scoped/objects/blobs.py +441 -0
- pyscoped-0.1.0/scoped/objects/export.py +242 -0
- pyscoped-0.1.0/scoped/objects/import_.py +168 -0
- pyscoped-0.1.0/scoped/objects/isolation.py +12 -0
- pyscoped-0.1.0/scoped/objects/manager.py +460 -0
- pyscoped-0.1.0/scoped/objects/models.py +91 -0
- pyscoped-0.1.0/scoped/objects/search.py +384 -0
- pyscoped-0.1.0/scoped/objects/versioning.py +32 -0
- pyscoped-0.1.0/scoped/registry/__init__.py +48 -0
- pyscoped-0.1.0/scoped/registry/base.py +374 -0
- pyscoped-0.1.0/scoped/registry/contracts.py +637 -0
- pyscoped-0.1.0/scoped/registry/decorators.py +138 -0
- pyscoped-0.1.0/scoped/registry/introspection.py +133 -0
- pyscoped-0.1.0/scoped/registry/kinds.py +101 -0
- pyscoped-0.1.0/scoped/registry/sqlite_store.py +105 -0
- pyscoped-0.1.0/scoped/registry/store.py +125 -0
- pyscoped-0.1.0/scoped/registry/templates.py +418 -0
- pyscoped-0.1.0/scoped/rules/__init__.py +53 -0
- pyscoped-0.1.0/scoped/rules/builtins.py +311 -0
- pyscoped-0.1.0/scoped/rules/compiler.py +121 -0
- pyscoped-0.1.0/scoped/rules/engine.py +535 -0
- pyscoped-0.1.0/scoped/rules/features.py +183 -0
- pyscoped-0.1.0/scoped/rules/models.py +213 -0
- pyscoped-0.1.0/scoped/rules/quotas.py +222 -0
- pyscoped-0.1.0/scoped/rules/rate_limit.py +190 -0
- pyscoped-0.1.0/scoped/rules/redaction.py +192 -0
- pyscoped-0.1.0/scoped/scheduling/__init__.py +27 -0
- pyscoped-0.1.0/scoped/scheduling/models.py +158 -0
- pyscoped-0.1.0/scoped/scheduling/queue.py +184 -0
- pyscoped-0.1.0/scoped/scheduling/scheduler.py +202 -0
- pyscoped-0.1.0/scoped/secrets/__init__.py +36 -0
- pyscoped-0.1.0/scoped/secrets/backend.py +107 -0
- pyscoped-0.1.0/scoped/secrets/leak_detection.py +105 -0
- pyscoped-0.1.0/scoped/secrets/models.py +273 -0
- pyscoped-0.1.0/scoped/secrets/policy.py +137 -0
- pyscoped-0.1.0/scoped/secrets/vault.py +517 -0
- pyscoped-0.1.0/scoped/storage/__init__.py +47 -0
- pyscoped-0.1.0/scoped/storage/archival.py +326 -0
- pyscoped-0.1.0/scoped/storage/blobs.py +129 -0
- pyscoped-0.1.0/scoped/storage/interface.py +132 -0
- pyscoped-0.1.0/scoped/storage/migrations/__init__.py +17 -0
- pyscoped-0.1.0/scoped/storage/migrations/base.py +42 -0
- pyscoped-0.1.0/scoped/storage/migrations/registry.py +113 -0
- pyscoped-0.1.0/scoped/storage/migrations/runner.py +253 -0
- pyscoped-0.1.0/scoped/storage/migrations/versions/__init__.py +8 -0
- pyscoped-0.1.0/scoped/storage/migrations/versions/m0001_initial_schema.py +799 -0
- pyscoped-0.1.0/scoped/storage/migrations/versions/m0002_contracts.py +67 -0
- pyscoped-0.1.0/scoped/storage/migrations/versions/m0003_blobs.py +73 -0
- pyscoped-0.1.0/scoped/storage/migrations/versions/m0004_scope_settings.py +50 -0
- pyscoped-0.1.0/scoped/storage/migrations/versions/m0005_search_index.py +57 -0
- pyscoped-0.1.0/scoped/storage/migrations/versions/m0006_templates.py +69 -0
- pyscoped-0.1.0/scoped/storage/migrations/versions/m0007_tiering_archival.py +93 -0
- pyscoped-0.1.0/scoped/storage/migrations/versions/m0008_events_webhooks.py +88 -0
- pyscoped-0.1.0/scoped/storage/migrations/versions/m0009_notifications.py +72 -0
- pyscoped-0.1.0/scoped/storage/migrations/versions/m0010_scheduling.py +73 -0
- pyscoped-0.1.0/scoped/storage/sqlite.py +1267 -0
- pyscoped-0.1.0/scoped/storage/tiering.py +423 -0
- pyscoped-0.1.0/scoped/temporal/__init__.py +16 -0
- pyscoped-0.1.0/scoped/temporal/constraints.py +146 -0
- pyscoped-0.1.0/scoped/temporal/reconstruction.py +131 -0
- pyscoped-0.1.0/scoped/temporal/rollback.py +359 -0
- pyscoped-0.1.0/scoped/tenancy/__init__.py +37 -0
- pyscoped-0.1.0/scoped/tenancy/config.py +419 -0
- pyscoped-0.1.0/scoped/tenancy/engine.py +233 -0
- pyscoped-0.1.0/scoped/tenancy/lifecycle.py +411 -0
- pyscoped-0.1.0/scoped/tenancy/models.py +208 -0
- pyscoped-0.1.0/scoped/tenancy/projection.py +203 -0
- pyscoped-0.1.0/scoped/testing/__init__.py +26 -0
- pyscoped-0.1.0/scoped/testing/auditor.py +244 -0
- pyscoped-0.1.0/scoped/testing/base.py +192 -0
- pyscoped-0.1.0/scoped/testing/fuzzer.py +278 -0
- pyscoped-0.1.0/scoped/testing/health.py +173 -0
- pyscoped-0.1.0/scoped/testing/introspection.py +147 -0
- pyscoped-0.1.0/scoped/testing/middleware.py +265 -0
- pyscoped-0.1.0/scoped/testing/reports.py +146 -0
- pyscoped-0.1.0/scoped/testing/rollback.py +247 -0
- pyscoped-0.1.0/scoped/types.py +217 -0
- pyscoped-0.1.0/tests/__init__.py +0 -0
- pyscoped-0.1.0/tests/conftest.py +32 -0
- pyscoped-0.1.0/tests/test_audit/__init__.py +0 -0
- pyscoped-0.1.0/tests/test_audit/test_models.py +100 -0
- pyscoped-0.1.0/tests/test_audit/test_query.py +189 -0
- pyscoped-0.1.0/tests/test_audit/test_writer.py +149 -0
- pyscoped-0.1.0/tests/test_compliance/__init__.py +0 -0
- pyscoped-0.1.0/tests/test_compliance/test_auditor.py +226 -0
- pyscoped-0.1.0/tests/test_compliance/test_base.py +180 -0
- pyscoped-0.1.0/tests/test_compliance/test_fuzzer.py +101 -0
- pyscoped-0.1.0/tests/test_compliance/test_health.py +123 -0
- pyscoped-0.1.0/tests/test_compliance/test_introspection.py +135 -0
- pyscoped-0.1.0/tests/test_compliance/test_middleware.py +238 -0
- pyscoped-0.1.0/tests/test_compliance/test_reports.py +123 -0
- pyscoped-0.1.0/tests/test_compliance/test_rollback.py +71 -0
- pyscoped-0.1.0/tests/test_connector/__init__.py +0 -0
- pyscoped-0.1.0/tests/test_connector/test_bridge.py +412 -0
- pyscoped-0.1.0/tests/test_connector/test_marketplace.py +434 -0
- pyscoped-0.1.0/tests/test_connector/test_models.py +227 -0
- pyscoped-0.1.0/tests/test_connector/test_protocol.py +186 -0
- pyscoped-0.1.0/tests/test_contrib/__init__.py +0 -0
- pyscoped-0.1.0/tests/test_contrib/conftest.py +23 -0
- pyscoped-0.1.0/tests/test_contrib/test_django/__init__.py +0 -0
- pyscoped-0.1.0/tests/test_contrib/test_django/conftest.py +15 -0
- pyscoped-0.1.0/tests/test_contrib/test_django/settings.py +23 -0
- pyscoped-0.1.0/tests/test_contrib/test_django/test_backend.py +185 -0
- pyscoped-0.1.0/tests/test_contrib/test_django/test_commands.py +101 -0
- pyscoped-0.1.0/tests/test_contrib/test_django/test_middleware.py +138 -0
- pyscoped-0.1.0/tests/test_contrib/test_django/urls.py +14 -0
- pyscoped-0.1.0/tests/test_contrib/test_fastapi/__init__.py +0 -0
- pyscoped-0.1.0/tests/test_contrib/test_fastapi/conftest.py +54 -0
- pyscoped-0.1.0/tests/test_contrib/test_fastapi/test_dependencies.py +84 -0
- pyscoped-0.1.0/tests/test_contrib/test_fastapi/test_middleware.py +34 -0
- pyscoped-0.1.0/tests/test_contrib/test_fastapi/test_router.py +81 -0
- pyscoped-0.1.0/tests/test_contrib/test_fastapi/test_schemas.py +105 -0
- pyscoped-0.1.0/tests/test_contrib/test_flask/__init__.py +0 -0
- pyscoped-0.1.0/tests/test_contrib/test_flask/conftest.py +53 -0
- pyscoped-0.1.0/tests/test_contrib/test_flask/test_extension.py +101 -0
- pyscoped-0.1.0/tests/test_contrib/test_mcp/__init__.py +0 -0
- pyscoped-0.1.0/tests/test_contrib/test_mcp/conftest.py +36 -0
- pyscoped-0.1.0/tests/test_contrib/test_mcp/test_resources.py +55 -0
- pyscoped-0.1.0/tests/test_contrib/test_mcp/test_tools.py +104 -0
- pyscoped-0.1.0/tests/test_deployments/__init__.py +0 -0
- pyscoped-0.1.0/tests/test_deployments/test_executor.py +237 -0
- pyscoped-0.1.0/tests/test_deployments/test_gates.py +136 -0
- pyscoped-0.1.0/tests/test_deployments/test_models.py +134 -0
- pyscoped-0.1.0/tests/test_deployments/test_rollback.py +124 -0
- pyscoped-0.1.0/tests/test_environments/__init__.py +0 -0
- pyscoped-0.1.0/tests/test_environments/test_container.py +144 -0
- pyscoped-0.1.0/tests/test_environments/test_lifecycle.py +267 -0
- pyscoped-0.1.0/tests/test_environments/test_models.py +165 -0
- pyscoped-0.1.0/tests/test_environments/test_snapshot.py +127 -0
- pyscoped-0.1.0/tests/test_events/__init__.py +0 -0
- pyscoped-0.1.0/tests/test_events/test_bus.py +307 -0
- pyscoped-0.1.0/tests/test_events/test_models.py +194 -0
- pyscoped-0.1.0/tests/test_events/test_subscriptions.py +247 -0
- pyscoped-0.1.0/tests/test_events/test_webhooks.py +225 -0
- pyscoped-0.1.0/tests/test_flow/__init__.py +0 -0
- pyscoped-0.1.0/tests/test_flow/test_engine.py +264 -0
- pyscoped-0.1.0/tests/test_flow/test_models.py +152 -0
- pyscoped-0.1.0/tests/test_flow/test_pipeline.py +227 -0
- pyscoped-0.1.0/tests/test_flow/test_promotion.py +241 -0
- pyscoped-0.1.0/tests/test_identity/__init__.py +0 -0
- pyscoped-0.1.0/tests/test_identity/test_context.py +105 -0
- pyscoped-0.1.0/tests/test_identity/test_principal.py +208 -0
- pyscoped-0.1.0/tests/test_identity/test_resolver.py +138 -0
- pyscoped-0.1.0/tests/test_integrations/__init__.py +0 -0
- pyscoped-0.1.0/tests/test_integrations/test_connectors.py +167 -0
- pyscoped-0.1.0/tests/test_integrations/test_hooks.py +323 -0
- pyscoped-0.1.0/tests/test_integrations/test_lifecycle.py +272 -0
- pyscoped-0.1.0/tests/test_integrations/test_models.py +179 -0
- pyscoped-0.1.0/tests/test_integrations/test_sandbox.py +177 -0
- pyscoped-0.1.0/tests/test_notifications/__init__.py +0 -0
- pyscoped-0.1.0/tests/test_notifications/test_notifications.py +540 -0
- pyscoped-0.1.0/tests/test_objects/__init__.py +0 -0
- pyscoped-0.1.0/tests/test_objects/test_blobs.py +455 -0
- pyscoped-0.1.0/tests/test_objects/test_import_export.py +479 -0
- pyscoped-0.1.0/tests/test_objects/test_isolation.py +16 -0
- pyscoped-0.1.0/tests/test_objects/test_manager.py +392 -0
- pyscoped-0.1.0/tests/test_objects/test_models.py +117 -0
- pyscoped-0.1.0/tests/test_objects/test_search.py +507 -0
- pyscoped-0.1.0/tests/test_objects/test_versioning.py +52 -0
- pyscoped-0.1.0/tests/test_registry/__init__.py +0 -0
- pyscoped-0.1.0/tests/test_registry/test_base.py +219 -0
- pyscoped-0.1.0/tests/test_registry/test_contracts.py +681 -0
- pyscoped-0.1.0/tests/test_registry/test_decorators.py +79 -0
- pyscoped-0.1.0/tests/test_registry/test_introspection.py +88 -0
- pyscoped-0.1.0/tests/test_registry/test_storage_backend.py +135 -0
- pyscoped-0.1.0/tests/test_registry/test_store.py +115 -0
- pyscoped-0.1.0/tests/test_registry/test_templates.py +579 -0
- pyscoped-0.1.0/tests/test_rules/__init__.py +0 -0
- pyscoped-0.1.0/tests/test_rules/test_builtins.py +97 -0
- pyscoped-0.1.0/tests/test_rules/test_compiler.py +106 -0
- pyscoped-0.1.0/tests/test_rules/test_engine.py +444 -0
- pyscoped-0.1.0/tests/test_rules/test_features.py +234 -0
- pyscoped-0.1.0/tests/test_rules/test_models.py +109 -0
- pyscoped-0.1.0/tests/test_rules/test_quotas.py +309 -0
- pyscoped-0.1.0/tests/test_rules/test_rate_limit.py +284 -0
- pyscoped-0.1.0/tests/test_rules/test_redaction.py +264 -0
- pyscoped-0.1.0/tests/test_scheduling/__init__.py +0 -0
- pyscoped-0.1.0/tests/test_scheduling/test_scheduling.py +425 -0
- pyscoped-0.1.0/tests/test_secrets/__init__.py +0 -0
- pyscoped-0.1.0/tests/test_secrets/test_backend.py +79 -0
- pyscoped-0.1.0/tests/test_secrets/test_leak_detection.py +136 -0
- pyscoped-0.1.0/tests/test_secrets/test_models.py +136 -0
- pyscoped-0.1.0/tests/test_secrets/test_policy.py +179 -0
- pyscoped-0.1.0/tests/test_secrets/test_vault.py +386 -0
- pyscoped-0.1.0/tests/test_storage/__init__.py +0 -0
- pyscoped-0.1.0/tests/test_storage/test_archival.py +344 -0
- pyscoped-0.1.0/tests/test_storage/test_blobs_backend.py +135 -0
- pyscoped-0.1.0/tests/test_storage/test_migrations.py +431 -0
- pyscoped-0.1.0/tests/test_storage/test_tiering.py +457 -0
- pyscoped-0.1.0/tests/test_temporal/__init__.py +0 -0
- pyscoped-0.1.0/tests/test_temporal/test_constraints.py +197 -0
- pyscoped-0.1.0/tests/test_temporal/test_reconstruction.py +189 -0
- pyscoped-0.1.0/tests/test_temporal/test_rollback.py +447 -0
- pyscoped-0.1.0/tests/test_tenancy/__init__.py +0 -0
- pyscoped-0.1.0/tests/test_tenancy/test_config.py +478 -0
- pyscoped-0.1.0/tests/test_tenancy/test_engine.py +214 -0
- pyscoped-0.1.0/tests/test_tenancy/test_lifecycle.py +247 -0
- pyscoped-0.1.0/tests/test_tenancy/test_models.py +94 -0
- pyscoped-0.1.0/tests/test_tenancy/test_projection.py +166 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.egg-info/
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
*.egg
|
|
9
|
+
|
|
10
|
+
# Virtual environments
|
|
11
|
+
.venv/
|
|
12
|
+
venv/
|
|
13
|
+
env/
|
|
14
|
+
|
|
15
|
+
# IDE
|
|
16
|
+
.idea/
|
|
17
|
+
.vscode/
|
|
18
|
+
*.swp
|
|
19
|
+
*.swo
|
|
20
|
+
*~
|
|
21
|
+
|
|
22
|
+
# OS
|
|
23
|
+
.DS_Store
|
|
24
|
+
Thumbs.db
|
|
25
|
+
|
|
26
|
+
# Testing
|
|
27
|
+
.pytest_cache/
|
|
28
|
+
.coverage
|
|
29
|
+
htmlcov/
|
|
30
|
+
.tox/
|
|
31
|
+
|
|
32
|
+
# Distribution
|
|
33
|
+
*.tar.gz
|
|
34
|
+
*.whl
|
pyscoped-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
# Scoped — Agent Instructions
|
|
2
|
+
|
|
3
|
+
## What This Is
|
|
4
|
+
|
|
5
|
+
Scoped is a universal object-isolation and tenancy framework for Python. It guarantees that anything built on it can be isolated, shared, traced, and rolled back — to any degree, at any time, by anyone with the right to do so.
|
|
6
|
+
|
|
7
|
+
**Repository:** https://github.com/kwip-info/scoped
|
|
8
|
+
**Python:** 3.11+ (developed on 3.13)
|
|
9
|
+
**Dependencies:** None required (SQLite backend included)
|
|
10
|
+
|
|
11
|
+
## Architecture
|
|
12
|
+
|
|
13
|
+
16 layers + a compliance engine (Layer 0). Every layer depends on the layers below it.
|
|
14
|
+
|
|
15
|
+
| Layer | Module | Purpose |
|
|
16
|
+
|-------|--------|---------|
|
|
17
|
+
| 0 | `scoped.testing` | Compliance engine — validates all invariants |
|
|
18
|
+
| 1 | `scoped.registry` | Universal construct registration (URNs) |
|
|
19
|
+
| 2 | `scoped.identity` | Generic principal machinery + ScopedContext |
|
|
20
|
+
| 3 | `scoped.objects` | Versioned, isolated data objects |
|
|
21
|
+
| 4 | `scoped.tenancy` | Scopes, membership, projection (the sharing primitive) |
|
|
22
|
+
| 5 | `scoped.rules` | Deny-overrides policy engine |
|
|
23
|
+
| 6 | `scoped.audit` | Hash-chained, immutable, append-only trace |
|
|
24
|
+
| 7 | `scoped.temporal` | Point-in-time reconstruction + cascading rollback |
|
|
25
|
+
| 8 | `scoped.environments` | Ephemeral workspaces |
|
|
26
|
+
| 9 | `scoped.flow` | Stages, pipelines, flow channels, promotions |
|
|
27
|
+
| 10 | `scoped.deployments` | Graduation to external targets with gate checks |
|
|
28
|
+
| 11 | `scoped.secrets` | Encrypted vault with ref-based zero-trust access |
|
|
29
|
+
| 12 | `scoped.integrations` | Sandboxed plugins, hooks, external systems |
|
|
30
|
+
| 13 | `scoped.connector` | Cross-org meshing, federation, marketplace |
|
|
31
|
+
| 14 | `scoped.events` | Asynchronous scoped event bus + webhooks |
|
|
32
|
+
| 15 | `scoped.notifications` | Principal-targeted messages from events/rules |
|
|
33
|
+
| 16 | `scoped.scheduling` | Recurring schedules, scoped job execution |
|
|
34
|
+
|
|
35
|
+
9 extensions enrich existing layers: A1 (Migrations), A2 (Contracts), A3 (Rule Extensions), A4 (Blobs), A5 (Config Hierarchy), A6 (Search), A7 (Templates), A8 (Tiering), A9 (Import/Export).
|
|
36
|
+
|
|
37
|
+
## The 10 Invariants
|
|
38
|
+
|
|
39
|
+
These are absolute. Never write code that violates them.
|
|
40
|
+
|
|
41
|
+
1. **Nothing exists without registration.** Every construct must have a registry entry with a URN.
|
|
42
|
+
2. **Nothing happens without identity.** Every operation requires a `ScopedContext` with an acting principal.
|
|
43
|
+
3. **Nothing is shared by default.** Every object starts creator-private. Sharing is always explicit via scope projection.
|
|
44
|
+
4. **Nothing happens without a trace.** Every action produces an immutable, hash-chained audit entry.
|
|
45
|
+
5. **Nothing is truly deleted.** Objects are tombstoned. Versions are retained. Audit is append-only.
|
|
46
|
+
6. **Deny always wins.** When rules conflict, DENY overrides ALLOW.
|
|
47
|
+
7. **Revocation is immediate.** Same-transaction enforcement, not eventual consistency.
|
|
48
|
+
8. **Everything is versioned.** Every mutation creates a new version.
|
|
49
|
+
9. **Everything is rollbackable.** Any action can be reversed to any point in time.
|
|
50
|
+
10. **Secrets never leak.** Values never appear in audit trails, snapshots, or connector traffic.
|
|
51
|
+
|
|
52
|
+
## Development
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install -e ".[dev]"
|
|
56
|
+
python3.13 -m pytest # run full suite (1,410 tests)
|
|
57
|
+
python3.13 -m pytest tests/test_objects/ # run one layer
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Test fixtures are in `tests/conftest.py`:
|
|
61
|
+
- `sqlite_backend` — in-memory SQLite, initialized with schema, closed after test
|
|
62
|
+
- `registry` — fresh Registry instance per test
|
|
63
|
+
- `_reset_global_state` (autouse) — resets singletons between tests
|
|
64
|
+
|
|
65
|
+
## Code Conventions
|
|
66
|
+
|
|
67
|
+
### Dataclasses
|
|
68
|
+
|
|
69
|
+
All models use `@dataclass(frozen=True, slots=True)` for immutability and memory efficiency. Mutable models (like `Principal`) use `@dataclass(slots=True)` without frozen.
|
|
70
|
+
|
|
71
|
+
### IDs and Timestamps
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from scoped.types import generate_id, now_utc
|
|
75
|
+
id = generate_id() # UUID hex string
|
|
76
|
+
ts = now_utc() # datetime in UTC
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Storage Backend
|
|
80
|
+
|
|
81
|
+
All persistence goes through `StorageBackend`. Never write raw SQL outside the storage layer.
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from scoped.storage.sqlite import SQLiteBackend
|
|
85
|
+
|
|
86
|
+
backend = SQLiteBackend(":memory:")
|
|
87
|
+
backend.initialize() # creates all tables — call once
|
|
88
|
+
|
|
89
|
+
# Queries
|
|
90
|
+
row = backend.fetch_one("SELECT ...", (param,))
|
|
91
|
+
rows = backend.fetch_all("SELECT ...", (param,))
|
|
92
|
+
|
|
93
|
+
# Writes (use transactions)
|
|
94
|
+
with backend.transaction() as tx:
|
|
95
|
+
tx.execute("INSERT ...", (param,))
|
|
96
|
+
tx.commit()
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Identity & Context
|
|
100
|
+
|
|
101
|
+
Every operation needs an acting principal. Use `ScopedContext` as a context manager:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from scoped.identity.context import ScopedContext
|
|
105
|
+
from scoped.identity.principal import PrincipalStore
|
|
106
|
+
|
|
107
|
+
principals = PrincipalStore(backend)
|
|
108
|
+
user = principals.create_principal(kind="user", display_name="Alice")
|
|
109
|
+
|
|
110
|
+
with ScopedContext(principal=user):
|
|
111
|
+
# All operations here are attributed to `user`
|
|
112
|
+
ctx = ScopedContext.current() # get active context
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Objects — Creator-Private by Default
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
from scoped.objects.manager import ScopedManager
|
|
119
|
+
|
|
120
|
+
manager = ScopedManager(backend)
|
|
121
|
+
|
|
122
|
+
# Create (creator-private)
|
|
123
|
+
obj, version = manager.create(object_type="document", owner_id=user.id, data={"title": "Draft"})
|
|
124
|
+
|
|
125
|
+
# Read (owner-only — returns None if not owner)
|
|
126
|
+
obj = manager.get(obj.id, principal_id=user.id)
|
|
127
|
+
|
|
128
|
+
# Update (creates new version)
|
|
129
|
+
obj, new_ver = manager.update(obj.id, principal_id=user.id, data={"title": "Final"})
|
|
130
|
+
|
|
131
|
+
# Soft delete
|
|
132
|
+
tombstone = manager.tombstone(obj.id, principal_id=user.id, reason="obsolete")
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Important:** `ScopedManager.get()` enforces **owner-only** access. Scope projections enable visibility through separate query paths (tenancy engine), not through the manager.
|
|
136
|
+
|
|
137
|
+
### Sharing via Scopes and Projections
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
from scoped.tenancy.lifecycle import ScopeLifecycle
|
|
141
|
+
from scoped.tenancy.projection import ProjectionManager
|
|
142
|
+
from scoped.tenancy.models import ScopeRole, AccessLevel
|
|
143
|
+
|
|
144
|
+
scopes = ScopeLifecycle(backend)
|
|
145
|
+
projections = ProjectionManager(backend)
|
|
146
|
+
|
|
147
|
+
# Create scope (owner auto-added as OWNER member)
|
|
148
|
+
scope = scopes.create_scope(name="Team Alpha", owner_id=user.id)
|
|
149
|
+
|
|
150
|
+
# Add members (requires granted_by and ScopeRole enum)
|
|
151
|
+
scopes.add_member(scope.id, principal_id=bob.id, role=ScopeRole.EDITOR, granted_by=user.id)
|
|
152
|
+
|
|
153
|
+
# Project object into scope (only owner can project)
|
|
154
|
+
projections.project(scope_id=scope.id, object_id=obj.id, projected_by=user.id)
|
|
155
|
+
|
|
156
|
+
# Revoke projection
|
|
157
|
+
projections.revoke_projection(scope_id=scope.id, object_id=obj.id, revoked_by=user.id)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Audit Trail
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
from scoped.audit.writer import AuditWriter
|
|
164
|
+
from scoped.types import ActionType
|
|
165
|
+
|
|
166
|
+
writer = AuditWriter(backend)
|
|
167
|
+
entry = writer.record(
|
|
168
|
+
actor_id=user.id,
|
|
169
|
+
action=ActionType.CREATE,
|
|
170
|
+
target_type="document",
|
|
171
|
+
target_id=obj.id,
|
|
172
|
+
)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Rules Engine
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from scoped.rules.engine import RuleStore, RuleEngine
|
|
179
|
+
from scoped.rules.models import RuleType, RuleEffect, BindingTargetType
|
|
180
|
+
|
|
181
|
+
store = RuleStore(backend)
|
|
182
|
+
rule = store.create_rule(
|
|
183
|
+
name="deny-external",
|
|
184
|
+
rule_type=RuleType.ACCESS,
|
|
185
|
+
effect=RuleEffect.DENY,
|
|
186
|
+
priority=10,
|
|
187
|
+
created_by=admin.id,
|
|
188
|
+
)
|
|
189
|
+
store.bind_rule(rule.id, target_type=BindingTargetType.SCOPE, target_id=scope.id, bound_by=admin.id)
|
|
190
|
+
|
|
191
|
+
engine = RuleEngine(backend)
|
|
192
|
+
result = engine.evaluate(action="read", principal_id=user.id, scope_id=scope.id)
|
|
193
|
+
# result.allowed -> bool
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Events
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
from scoped.events import EventBus, EventType, SubscriptionManager
|
|
200
|
+
|
|
201
|
+
bus = EventBus(backend)
|
|
202
|
+
event = bus.emit(
|
|
203
|
+
EventType.OBJECT_CREATED,
|
|
204
|
+
actor_id=user.id,
|
|
205
|
+
target_type="document",
|
|
206
|
+
target_id=doc.id,
|
|
207
|
+
)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Notifications
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
from scoped.notifications import NotificationEngine, NotificationChannel
|
|
214
|
+
|
|
215
|
+
engine = NotificationEngine(backend)
|
|
216
|
+
rule = engine.create_rule(
|
|
217
|
+
name="Deploy Alerts",
|
|
218
|
+
owner_id=admin.id,
|
|
219
|
+
event_types=["deployment_completed"],
|
|
220
|
+
recipient_ids=[ops.id],
|
|
221
|
+
channel=NotificationChannel.IN_APP,
|
|
222
|
+
)
|
|
223
|
+
notifications = engine.process_event(event)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Scheduling
|
|
227
|
+
|
|
228
|
+
```python
|
|
229
|
+
from scoped.scheduling import Scheduler, JobQueue
|
|
230
|
+
|
|
231
|
+
scheduler = Scheduler(backend)
|
|
232
|
+
action = scheduler.create_action(
|
|
233
|
+
name="Nightly cleanup",
|
|
234
|
+
owner_id=admin.id,
|
|
235
|
+
action_type="cleanup",
|
|
236
|
+
action_config={"max_age_hours": 24},
|
|
237
|
+
next_run_at=tomorrow_2am,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
queue = JobQueue(backend, executor=my_executor)
|
|
241
|
+
job = queue.enqueue(name="Cleanup", action_type="cleanup", action_config={}, owner_id=admin.id)
|
|
242
|
+
queue.run_next()
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Common Pitfalls
|
|
246
|
+
|
|
247
|
+
- **`create_scope()` not `create()`** — `ScopeLifecycle` method is `create_scope()`
|
|
248
|
+
- **`create_principal()` not `create()`** — `PrincipalStore` method is `create_principal()`
|
|
249
|
+
- **`add_member()` requires `granted_by` and `ScopeRole` enum** — not a string role
|
|
250
|
+
- **`revoke_projection()` not `revoke()`** — `ProjectionManager` method name
|
|
251
|
+
- **FK constraints in tests** — to create orphaned data for compliance testing, wrap with `PRAGMA foreign_keys = OFF/ON`
|
|
252
|
+
- **`ScopedManager.get()` is owner-only** — projections don't grant manager-level access
|
|
253
|
+
- **Rollback traces vs DB state** — `RollbackExecutor._apply_rollback_state()` only modifies DB for `target_type == "object"` literally; `ScopedManager` uses the actual object_type (e.g., "document") as target_type
|
|
254
|
+
- **`backend.initialize()` required** — always call after creating `SQLiteBackend`
|
|
255
|
+
|
|
256
|
+
## Framework Adapters (`scoped.contrib`)
|
|
257
|
+
|
|
258
|
+
All adapters are optional — install with extras: `pip install scoped[django]`, etc.
|
|
259
|
+
|
|
260
|
+
### Django
|
|
261
|
+
|
|
262
|
+
```python
|
|
263
|
+
# settings.py
|
|
264
|
+
INSTALLED_APPS = ["scoped.contrib.django"]
|
|
265
|
+
MIDDLEWARE = ["scoped.contrib.django.middleware.ScopedContextMiddleware"]
|
|
266
|
+
SCOPED_PRINCIPAL_HEADER = "HTTP_X_SCOPED_PRINCIPAL_ID"
|
|
267
|
+
|
|
268
|
+
# Management commands: scoped_health, scoped_audit, scoped_compliance
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### FastAPI
|
|
272
|
+
|
|
273
|
+
```python
|
|
274
|
+
from fastapi import FastAPI
|
|
275
|
+
from scoped.contrib.fastapi.middleware import ScopedContextMiddleware
|
|
276
|
+
from scoped.contrib.fastapi.router import router as scoped_router
|
|
277
|
+
from scoped.contrib.fastapi.dependencies import get_scoped_context, get_principal
|
|
278
|
+
|
|
279
|
+
app = FastAPI()
|
|
280
|
+
app.add_middleware(ScopedContextMiddleware, backend=backend)
|
|
281
|
+
app.include_router(scoped_router) # adds /scoped/health, /scoped/audit
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Flask
|
|
285
|
+
|
|
286
|
+
```python
|
|
287
|
+
from flask import Flask
|
|
288
|
+
from scoped.contrib.flask.extension import ScopedExtension
|
|
289
|
+
from scoped.contrib.flask.admin import admin_bp
|
|
290
|
+
|
|
291
|
+
app = Flask(__name__)
|
|
292
|
+
scoped = ScopedExtension(app) # auto-inits backend, injects g.scoped_context
|
|
293
|
+
app.register_blueprint(admin_bp) # adds /scoped/health, /scoped/audit
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### MCP (Model Context Protocol)
|
|
297
|
+
|
|
298
|
+
```python
|
|
299
|
+
from scoped.contrib.mcp.server import create_scoped_server
|
|
300
|
+
|
|
301
|
+
mcp = create_scoped_server(backend) # registers tools + resources
|
|
302
|
+
mcp.run()
|
|
303
|
+
# Tools: create_principal, create_object, get_object, create_scope, list_audit, health_check
|
|
304
|
+
# Resources: scoped://principals, scoped://health, scoped://audit/recent
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## Project Structure
|
|
308
|
+
|
|
309
|
+
```
|
|
310
|
+
scoped/
|
|
311
|
+
__init__.py # version
|
|
312
|
+
conf.py # ScopedConfig
|
|
313
|
+
exceptions.py # all framework exceptions
|
|
314
|
+
types.py # generate_id, now_utc, URN, ActionType, Lifecycle
|
|
315
|
+
|
|
316
|
+
registry/ # Layer 1
|
|
317
|
+
identity/ # Layer 2
|
|
318
|
+
objects/ # Layer 3 + blobs, search, import/export
|
|
319
|
+
tenancy/ # Layer 4 + config hierarchy
|
|
320
|
+
rules/ # Layer 5 + redaction, rate limits, quotas, feature flags
|
|
321
|
+
audit/ # Layer 6
|
|
322
|
+
temporal/ # Layer 7
|
|
323
|
+
environments/ # Layer 8
|
|
324
|
+
flow/ # Layer 9
|
|
325
|
+
deployments/ # Layer 10
|
|
326
|
+
secrets/ # Layer 11
|
|
327
|
+
integrations/ # Layer 12
|
|
328
|
+
connector/ # Layer 13 + marketplace/
|
|
329
|
+
events/ # Layer 14
|
|
330
|
+
notifications/ # Layer 15
|
|
331
|
+
scheduling/ # Layer 16
|
|
332
|
+
testing/ # Layer 0 (compliance)
|
|
333
|
+
storage/ # backends + migrations + tiering
|
|
334
|
+
contrib/ # Phase D: Framework adapters
|
|
335
|
+
_base.py # shared utilities (build_services, resolve_principal)
|
|
336
|
+
django/ # D1: Django adapter
|
|
337
|
+
fastapi/ # D2: FastAPI adapter
|
|
338
|
+
flask/ # D3: Flask adapter
|
|
339
|
+
mcp/ # D4: MCP adapter
|
|
340
|
+
|
|
341
|
+
tests/
|
|
342
|
+
conftest.py # shared fixtures
|
|
343
|
+
test_registry/ # ... one directory per layer
|
|
344
|
+
test_compliance/
|
|
345
|
+
test_contrib/ # adapter tests (D1-D4)
|
|
346
|
+
test_integration/ # end-to-end tests
|
|
347
|
+
|
|
348
|
+
docs/
|
|
349
|
+
architecture.md # full architecture document
|
|
350
|
+
layers/ # per-layer documentation (00-16)
|
|
351
|
+
extensions/ # A1-A9 extension docs
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
## Documentation
|
|
355
|
+
|
|
356
|
+
- [Architecture Overview](docs/architecture.md) — the full system design
|
|
357
|
+
- [Layer Docs](docs/layers/) — one file per layer (00-compliance through 16-scheduling)
|
|
358
|
+
- [Extension Docs](docs/extensions/) — A1-migrations through A9-import-export
|
|
359
|
+
|
|
360
|
+
## Using Scoped in Another Project
|
|
361
|
+
|
|
362
|
+
When building an application on Scoped, the typical bootstrap is:
|
|
363
|
+
|
|
364
|
+
```python
|
|
365
|
+
from scoped.storage.sqlite import SQLiteBackend
|
|
366
|
+
from scoped.identity.principal import PrincipalStore
|
|
367
|
+
from scoped.objects.manager import ScopedManager
|
|
368
|
+
from scoped.tenancy.lifecycle import ScopeLifecycle
|
|
369
|
+
from scoped.tenancy.projection import ProjectionManager
|
|
370
|
+
from scoped.audit.writer import AuditWriter
|
|
371
|
+
from scoped.rules.engine import RuleStore, RuleEngine
|
|
372
|
+
|
|
373
|
+
# 1. Initialize storage
|
|
374
|
+
backend = SQLiteBackend("app.db")
|
|
375
|
+
backend.initialize()
|
|
376
|
+
|
|
377
|
+
# 2. Create stores
|
|
378
|
+
principals = PrincipalStore(backend)
|
|
379
|
+
manager = ScopedManager(backend)
|
|
380
|
+
scopes = ScopeLifecycle(backend)
|
|
381
|
+
projections = ProjectionManager(backend)
|
|
382
|
+
audit = AuditWriter(backend)
|
|
383
|
+
rules = RuleStore(backend)
|
|
384
|
+
|
|
385
|
+
# 3. Create a principal (the acting user)
|
|
386
|
+
user = principals.create_principal(kind="user", display_name="Alice")
|
|
387
|
+
|
|
388
|
+
# 4. Everything after this requires ScopedContext
|
|
389
|
+
from scoped.identity.context import ScopedContext
|
|
390
|
+
|
|
391
|
+
with ScopedContext(principal=user):
|
|
392
|
+
obj, ver = manager.create(object_type="task", owner_id=user.id, data={"title": "Hello"})
|
|
393
|
+
scope = scopes.create_scope(name="My Team", owner_id=user.id)
|
|
394
|
+
projections.project(scope_id=scope.id, object_id=obj.id, projected_by=user.id)
|
|
395
|
+
```
|
pyscoped-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 kwip-info
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|