matrx-orm 3.1.174__tar.gz → 3.1.176__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.
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/.gitignore +10 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/PKG-INFO +2 -2
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/pooler_role.py +1 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/rls_session.py +30 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/__init__.py +15 -0
- matrx_orm-3.1.176/matrx_orm/secrets_battery/backup_restore.py +286 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/mutation_receipts.py +39 -2
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/org_service.py +7 -4
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/README.md +11 -1
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/fallback.py +66 -25
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/lifecycle.py +1 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/managed.py +5 -2
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/session.py +27 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/pyproject.toml +2 -2
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_rls_session_scope.py +58 -7
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_flush_individual_fallback.py +34 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_record_error_bounded_insert.py +9 -1
- matrx_orm-3.1.176/tests/test_backup_alias_conflict.py +44 -0
- matrx_orm-3.1.176/tests/test_backup_restore_live.py +192 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_native_passkey_receipts.py +23 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/AGENTS.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/BASE_CLASS_METHODS.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/CLAUDE.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/MODEL_API.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/README.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/database/orm/extended/managers/ai_model_base.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/docs/migrations.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/FEATURE.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/MODULE_README.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/__init__.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/adapters/MODULE_README.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/adapters/__init__.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/adapters/async_postgresql.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/adapters/base_adapter.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/adapters/postgrest_client_adapter.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/adapters/supabase_adapter.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/admin/FEATURE.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/admin/__init__.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/admin/router.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/api/__init__.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/api/auth.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/api/config.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/api/handlers.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/api/protocol.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/api/server.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/associations.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/cache_debug.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/catalog.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/catalog_sql.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/client/MODULE_README.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/client/__init__.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/client/postgres_connection.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/client/postgrest.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/client/supabase_auth.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/client/supabase_config.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/client/supabase_manager.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/EXTENDED-TASK.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/FEATURE.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/MODULE_README.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/RELATIONS-TASKS.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/TASKS.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/__init__.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/async_db_manager.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/base.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/config.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/connections.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/db_function.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/diagnostics.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/expressions.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/extended.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/fields.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/introspection.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/listen.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/loop_filters.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/model_dto.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/model_view.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/one_database.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/paginator.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/pool_maintenance.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/pool_watch.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/pydantic_bridge.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/registry.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/relations.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/resilience.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/session_context.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/signals.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/tls.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/transaction.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/types.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/write_queue.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/write_retry.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/entity.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/error_handling.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/exceptions.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/extended/__init__.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/extended/app_error_handler.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/identity_rule.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/local_sqlite.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/middleware/__init__.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/middleware/base.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/migrations/MODULE_README.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/migrations/__init__.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/migrations/cli.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/migrations/ddl.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/migrations/diff.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/migrations/executor.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/migrations/integration.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/migrations/loader.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/migrations/operations.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/migrations/state.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/migrations/table_filter.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/no_org_assignment.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/FEATURE.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/MODULE_README.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/__init__.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/bulk_update_values.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/catalog_select.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/conflict.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/conflict_writes.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/create.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/db_functions.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/delete.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/dynamic_admin.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/dynamic_crud.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/graph_walk.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/latest_rows.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/queue_claim.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/read.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/relational_backfill.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/staging_load.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/update.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/platform_access.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/platform_db.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/py.typed +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/pytest_lock_guard.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/pytest_plugin.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/python_sql/MODULE_README.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/python_sql/__init__.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/python_sql/db_objects.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/python_sql/table_detailed_relationships.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/query/FEATURE.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/query/MODULE_README.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/query/__init__.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/query/builder.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/query/executor.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/resources/__init__.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/resources/additional-assigners-2026-09-12.json +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/resources/org_assignment_trigger_baseline.json +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/FEATURE.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/MODULE_README.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/__init__.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/code_handler.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/codegen_writer.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/columns.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/common.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/diff_preview.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/entity_capabilities.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/generator.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/helpers/__init__.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/package_wiring.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/relationships.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/runner.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/schema.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/schema_manager.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/tables.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/views.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/FEATURE.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/attachments.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/crypto.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/host_authorization.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/items.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/model.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/native_enable.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/native_lifecycle.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/native_passkeys.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/native_source.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/service.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/__init__.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/coalesce.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/dag.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/errors.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/flush.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/op.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/reads.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/telemetry.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/sql_executor/MODULE_README.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/sql_executor/__init__.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/sql_executor/executor.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/sql_executor/queries.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/sql_executor/registry.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/sql_executor/types.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/sql_executor/utils.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/state.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/utils/__init__.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/utils/sql_utils.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/utils/type_converters.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/write_actor.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/scripts/git-branches.sh +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/scripts/publish.sh +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/scripts/release.sh +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/MODULE_README.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/__init__.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/conftest.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/MODULE_README.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/__init__.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_adapter_registry.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_admin_db_columns.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_admin_response_rendering.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_admin_search_negation.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_admin_search_sentinels.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_advisory_lock.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_agent_message.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_api_auth.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_api_config.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_api_handlers.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_api_protocol.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_array_agg_order_by.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_association_trigger_actor_naming.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_associations.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_associations_wiring.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_bulk_hydration_offload.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_bulk_update_by_pk.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_bulk_upsert_increment_set_fields.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_cache_debug.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_call_function.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_case_when.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_catalog.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_committed_row_guard.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_composite_pk_filter.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_composite_pk_write_paths.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_config.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_conflict_target.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_conflict_writes.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_connection_codecs.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_connection_poison_guard.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_connection_primitives.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_count_composite_distinct.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_count_limit_param_leak.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_db_function_field.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_ddl_generator.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_deadlock_retry.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_derived_table.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_dirty_tracking.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_dynamic_crud.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_exceptions.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_execute_query_integrity_mapping.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_expression_primitives.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_fields.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_filter_jsonb_agg_array_index.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_from_alias.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_fts.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_func_expression_args.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_graph_walk.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_having_annotate_alias.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_insert_ignore_and_admin_primitives.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_introspect_rls_policies.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_join_and_aggregate.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_latest_rows.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_listen_notify.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_local_sqlite.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_lock_contention_retry.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_managed_write_guard.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_matrx_entity.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_migration_diff_types.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_migration_loader.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_model_instance.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_model_meta.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_optimistic_concurrency.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_order_by_expression.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_org_assignment_inventory.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_orm_banners_are_client_safe.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_parameter_name_hydration.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_patch_jsonb_path.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_percentile_cont.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_persisted_count.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_pool_holder_census.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_pool_loop_recreation.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_pool_maintenance.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_postgrest_filters.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_pydantic_bridge.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_query_builder.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_query_error_detail.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_query_executor_sql.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_query_timeout.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_querybuilder_clone_semantics.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_queue_claim.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_read_connection_retry.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_registry.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_registry_multi_database.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_registry_multi_schema.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_relational_backfill.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_relations.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_resilience.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_schema_exists.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_session_advisory_lock.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_standalone_write_session_context.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_state_cache.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_subquery_filter_raw.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_subquery_in_filter.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_subquery_nested_expressions.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_supabase_auth.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_supabase_config.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_tls.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_transaction_database_identity.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_typed_join_projection.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_typed_predicates.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_update_case_expression.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_update_subquery.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_update_with_rebase.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_upsert_default_leak.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_upsert_with_conflict.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_values_jsonb_decode.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_watched_lifecycle.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_write_queue_deadline.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_write_retry_semantics.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/MODULE_README.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/__init__.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/conftest.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/test_bulk_ops.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/test_cache_integration.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/test_crud.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/test_foreign_keys.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/test_m2m.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/test_manager.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/test_migrations_live.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/test_query_execution.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/test_schema_diff.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/verify_committed_row_guard_pg17.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/live_pooler_role.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/native_source_cases.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/native_source_oracle.rs +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project/.env.example +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project/README.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project/__init__.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project/generate.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project/generated/.gitkeep +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project/matrx_orm.yaml +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project/test_schema_generation.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project_desktop/.env.example +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project_desktop/README.md +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project_desktop/__init__.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project_desktop/client_example.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project_desktop/client_example.ts +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project_desktop/client_supabase_example.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project_desktop/server.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/entity_tests.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_base_generation.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_composite_pk_fk_generation.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_composite_unique_generation.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_dto_identity_generation.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_entity_capability_generation.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_enum_ordering.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_generate_schema.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_junction_analysis.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_multi_schema_output.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_schema_analysis.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_sql_expression_defaults.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_table_relationships_query.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_view_generation.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema_builder/test_array_default_parsing.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema_builder/test_focused_generation_plan.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema_builder/test_host_models_aggregator.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/__init__.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/conftest.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_cache_no_downgrade.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_capture_poison_proof.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_coalesce.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_dag.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_disk_spill_fallback.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_governed_write.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_managed.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_op_enqueue_site.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_reads.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_record_error_storm.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_session.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_spill_guard_plugin.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_array_arg_element_type.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_attachment_mutation_serialization.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_database_name_alias.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_db_functions.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_dynamic_select_unregistered.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_immutable_insert_or_exact_verify.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_keyed_digest.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_model_cls_refactor.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_native_enable.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_native_lifecycle.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_native_lifecycle_fences.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_native_source.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_no_blocking_asyncpg_tls.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_one_database.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_order_by_desc_nulls_last.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_pooler_role_guard.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_secret_audit_names_its_organization.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_secrets_battery.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_secrets_minting_keys.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_shared_lock_guard.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_sql_param_casts.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_sync_twins_guard.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_user_visible_lookup.py +0 -0
- {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_vault_aggregate_recovery_stateful.py +0 -0
|
@@ -197,6 +197,14 @@ cython_debug/
|
|
|
197
197
|
# Frontend pnpm workspace (apps/) — node_modules at the workspace root and any
|
|
198
198
|
# member, plus Vite caches and build output. The unified lockfile (apps/pnpm-lock.yaml)
|
|
199
199
|
# IS committed; everything below is regenerated.
|
|
200
|
+
#
|
|
201
|
+
# 🚨 BOTH SPELLINGS. `node_modules/` with the slash matches a DIRECTORY only, so
|
|
202
|
+
# a SYMLINK named `node_modules` slipped straight past it: five of them — each
|
|
203
|
+
# an absolute path into one machine's checkout — were committed on main and
|
|
204
|
+
# broke `pnpm install --frozen-lockfile` on every CI runner, where the symlink
|
|
205
|
+
# is dangling and pnpm's `mkdir` of it answers ENOENT. Every package release was
|
|
206
|
+
# blocked by it. The bare spelling below matches the symlink too.
|
|
207
|
+
node_modules
|
|
200
208
|
node_modules/
|
|
201
209
|
apps/**/.vite/
|
|
202
210
|
apps/**/dist/
|
|
@@ -328,3 +336,5 @@ apps/shared/.kit-*-witness.*/
|
|
|
328
336
|
# They live at .wt/<slug> (disk-groom). Committing the folder would add a
|
|
329
337
|
# second copy of the entire tree and a symlink into common-docs.
|
|
330
338
|
.wt/
|
|
339
|
+
.matrx/fixer-dispatch.json
|
|
340
|
+
.matrx/fixer-ledger.json
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: matrx-orm
|
|
3
|
-
Version: 3.1.
|
|
3
|
+
Version: 3.1.176
|
|
4
4
|
Summary: Async-first PostgreSQL ORM with bidirectional migrations, schema introspection, many-to-many relationships, and built-in state caching
|
|
5
5
|
Project-URL: Homepage, https://github.com/AI-Matrix-Engine/aidream-current
|
|
6
6
|
Project-URL: Repository, https://github.com/AI-Matrix-Engine/aidream-current
|
|
@@ -26,7 +26,7 @@ Requires-Dist: asyncpg>=0.31.0
|
|
|
26
26
|
Requires-Dist: cbor2<7,>=6.1.3
|
|
27
27
|
Requires-Dist: cryptography>=43.0
|
|
28
28
|
Requires-Dist: gitpython>=3.1.40
|
|
29
|
-
Requires-Dist: matrx-utils>=2.0.
|
|
29
|
+
Requires-Dist: matrx-utils>=2.0.51
|
|
30
30
|
Requires-Dist: psycopg-pool>=3.2.5
|
|
31
31
|
Requires-Dist: psycopg[binary]>=3.2.5
|
|
32
32
|
Requires-Dist: python-dotenv>=1.0
|
|
@@ -203,6 +203,7 @@ async def emit_contamination(event: EmptyReadContaminationEvent) -> None:
|
|
|
203
203
|
),
|
|
204
204
|
kind="pooler_role_contamination",
|
|
205
205
|
source_app="matrx-orm",
|
|
206
|
+
source_feature="system",
|
|
206
207
|
route=f"orm:{event.config_name}",
|
|
207
208
|
payload={
|
|
208
209
|
"config_name": event.config_name,
|
|
@@ -51,6 +51,23 @@ _ROLE_RE = re.compile(r"^[a-z_][a-z0-9_]*$")
|
|
|
51
51
|
# agent query holding a pooled connection. Callers may override or disable (None).
|
|
52
52
|
DEFAULT_STATEMENT_TIMEOUT_MS = 30_000
|
|
53
53
|
|
|
54
|
+
# Default ceiling on how long an RLS session may sit IDLE INSIDE ITS TRANSACTION before
|
|
55
|
+
# Postgres ends it. Measured on the main database 2026-09-20, lane TIDY: one backend sat
|
|
56
|
+
# `idle in transaction` on `Client/ClientRead` for 89.7 seconds holding AccessShareLock on
|
|
57
|
+
# every partition and index of `custom.record`, `custom.merge_field_provenance` and
|
|
58
|
+
# `custom.anon_form`. A campaign suite then asked for AccessExclusiveLock on one of those
|
|
59
|
+
# relations and queued behind it — and because Postgres's lock queue is FIFO, every
|
|
60
|
+
# ordinary reader that arrived after that queued behind the DDL, four backends deep within
|
|
61
|
+
# seconds, none of which conflicted with the lock actually being held.
|
|
62
|
+
#
|
|
63
|
+
# Nothing legitimate is idle inside a transaction for thirty seconds: the whole point of
|
|
64
|
+
# holding a session open for a turn is that work is happening in it. What IS idle that long
|
|
65
|
+
# is a turn whose caller is waiting on a model, a network hop or a person — and a
|
|
66
|
+
# connection holding store locks while it waits is a stall for everybody else. This ends
|
|
67
|
+
# such a session instead, releasing every lock; the caller sees a named error rather than
|
|
68
|
+
# the rest of the platform seeing a mystery.
|
|
69
|
+
DEFAULT_IDLE_IN_TRANSACTION_TIMEOUT_MS = 30_000
|
|
70
|
+
|
|
54
71
|
|
|
55
72
|
@asynccontextmanager
|
|
56
73
|
async def rls_session(
|
|
@@ -60,6 +77,7 @@ async def rls_session(
|
|
|
60
77
|
database: str | None = None,
|
|
61
78
|
statement_timeout_ms: int | None = DEFAULT_STATEMENT_TIMEOUT_MS,
|
|
62
79
|
lock_timeout_ms: int | None = None,
|
|
80
|
+
idle_in_transaction_timeout_ms: int | None = DEFAULT_IDLE_IN_TRANSACTION_TIMEOUT_MS,
|
|
63
81
|
) -> AsyncIterator[None]:
|
|
64
82
|
"""Run the enclosed block as ``role`` with ``request.jwt.claims`` set to ``claims``.
|
|
65
83
|
|
|
@@ -72,6 +90,13 @@ async def rls_session(
|
|
|
72
90
|
``statement_timeout`` does not bound it, and an unbounded waiter queues behind the
|
|
73
91
|
dump or sweep that is already running while blocking everyone behind it. Like the
|
|
74
92
|
role and the claims it is ``SET LOCAL`` — it cannot outlive the transaction.
|
|
93
|
+
|
|
94
|
+
``idle_in_transaction_timeout_ms`` bounds how long this session may sit idle INSIDE
|
|
95
|
+
its transaction while holding every lock it has taken. It rides the same one round
|
|
96
|
+
trip as the other three settings, so it is free, and it is what stops one waiting
|
|
97
|
+
caller becoming a stall for every reader of the relations it touched — see
|
|
98
|
+
:data:`DEFAULT_IDLE_IN_TRANSACTION_TIMEOUT_MS` for the 89.7-second case that put it
|
|
99
|
+
here. ``None`` leaves the server default alone.
|
|
75
100
|
"""
|
|
76
101
|
if not _ROLE_RE.match(role):
|
|
77
102
|
raise DatabaseError(
|
|
@@ -119,6 +144,11 @@ async def rls_session(
|
|
|
119
144
|
if lock_timeout_ms is not None:
|
|
120
145
|
bound.append(str(int(lock_timeout_ms)))
|
|
121
146
|
settings.append(f"set_config('lock_timeout', ${len(bound)}, true)")
|
|
147
|
+
if idle_in_transaction_timeout_ms is not None:
|
|
148
|
+
bound.append(str(int(idle_in_transaction_timeout_ms)))
|
|
149
|
+
settings.append(
|
|
150
|
+
f"set_config('idle_in_transaction_session_timeout', ${len(bound)}, true)"
|
|
151
|
+
)
|
|
122
152
|
bound.append(role)
|
|
123
153
|
settings.append(f"set_config('role', ${len(bound)}, true)")
|
|
124
154
|
await conn.execute("select " + ", ".join(settings), *bound)
|
|
@@ -167,6 +167,15 @@ from .service import (
|
|
|
167
167
|
)
|
|
168
168
|
|
|
169
169
|
|
|
170
|
+
from .backup_restore import (
|
|
171
|
+
BackupAttachmentSpec,
|
|
172
|
+
BackupRestoreAliasConflictError,
|
|
173
|
+
BackupRestoreResult,
|
|
174
|
+
create_backup_record_quarantined,
|
|
175
|
+
)
|
|
176
|
+
from .mutation_receipts import complete_backup_restore_receipt, reserve_backup_restore_receipt
|
|
177
|
+
|
|
178
|
+
|
|
170
179
|
def configure_secrets(
|
|
171
180
|
*,
|
|
172
181
|
db_config_name: str | None = None,
|
|
@@ -192,6 +201,12 @@ def configure_secrets(
|
|
|
192
201
|
|
|
193
202
|
|
|
194
203
|
__all__ = [
|
|
204
|
+
"BackupAttachmentSpec",
|
|
205
|
+
"BackupRestoreResult",
|
|
206
|
+
"BackupRestoreAliasConflictError",
|
|
207
|
+
"create_backup_record_quarantined",
|
|
208
|
+
"complete_backup_restore_receipt",
|
|
209
|
+
"reserve_backup_restore_receipt",
|
|
195
210
|
"configure_secrets",
|
|
196
211
|
"configure_encryption",
|
|
197
212
|
"fernet_for_key",
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
"""Atomic personal backup restoration with execution disabled from insertion."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import AsyncIterator
|
|
6
|
+
from contextlib import asynccontextmanager
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from typing import Literal
|
|
9
|
+
from uuid import UUID
|
|
10
|
+
|
|
11
|
+
from matrx_orm import transaction
|
|
12
|
+
from matrx_orm.exceptions import describe_db_exception
|
|
13
|
+
|
|
14
|
+
from .attachments import _validate_content, _validate_text
|
|
15
|
+
from .crypto import encrypt_bytes, encrypt_value, mask_value
|
|
16
|
+
from .items import (
|
|
17
|
+
NON_TRANSFERABLE_FIELD_KEYS,
|
|
18
|
+
FieldSpec,
|
|
19
|
+
_metadata_kwargs,
|
|
20
|
+
_refuse_reserved_generic_definition,
|
|
21
|
+
_refuse_reserved_generic_field,
|
|
22
|
+
_validate_field_key,
|
|
23
|
+
)
|
|
24
|
+
from .model import (
|
|
25
|
+
PACKAGE_DB_NAME,
|
|
26
|
+
CredentialAttachment,
|
|
27
|
+
CredentialItem,
|
|
28
|
+
PasskeyCredential,
|
|
29
|
+
UserSecret,
|
|
30
|
+
UserSecretGrant,
|
|
31
|
+
)
|
|
32
|
+
from .mutation_receipts import (
|
|
33
|
+
complete_backup_restore_receipt,
|
|
34
|
+
reserve_backup_restore_receipt,
|
|
35
|
+
)
|
|
36
|
+
from .org_service import record_secret_audit
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass(frozen=True, repr=False)
|
|
40
|
+
class BackupAttachmentSpec:
|
|
41
|
+
"""Portable file content; never rendered into logs or exception messages."""
|
|
42
|
+
|
|
43
|
+
label: str
|
|
44
|
+
description: str | None
|
|
45
|
+
file_name: str
|
|
46
|
+
media_type: str
|
|
47
|
+
content: bytes
|
|
48
|
+
source_handling: Literal["visible", "revealable"]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass(frozen=True)
|
|
52
|
+
class BackupRestoreResult:
|
|
53
|
+
item_id: str
|
|
54
|
+
replayed: bool
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class BackupRestoreAliasConflictError(Exception):
|
|
58
|
+
"""A restored alias is reserved; the entire record was rolled back."""
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@asynccontextmanager
|
|
62
|
+
async def _restore_transaction() -> AsyncIterator[None]:
|
|
63
|
+
try:
|
|
64
|
+
async with transaction(PACKAGE_DB_NAME):
|
|
65
|
+
yield
|
|
66
|
+
except Exception as error:
|
|
67
|
+
# Translate only after rollback; never expose value-bearing DB diagnostics.
|
|
68
|
+
info = describe_db_exception(error)
|
|
69
|
+
if info.sqlstate == "23505" and info.constraint == "user_secrets_unique_personal_key":
|
|
70
|
+
raise BackupRestoreAliasConflictError(
|
|
71
|
+
"An environment alias from this backup is already in use."
|
|
72
|
+
) from None
|
|
73
|
+
raise
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
async def create_backup_record_quarantined(
|
|
77
|
+
*,
|
|
78
|
+
actor_id: str,
|
|
79
|
+
audit_organization_id: str,
|
|
80
|
+
mutation_id: str,
|
|
81
|
+
request_fingerprint: bytes,
|
|
82
|
+
display_name: str,
|
|
83
|
+
definition_key: str,
|
|
84
|
+
definition_version: int,
|
|
85
|
+
provider_key: str | None,
|
|
86
|
+
description: str | None,
|
|
87
|
+
tags: list[str],
|
|
88
|
+
login_urls: list[str],
|
|
89
|
+
uri_match_mode: str | None,
|
|
90
|
+
notes: str | None,
|
|
91
|
+
non_secret_fields: list[dict[str, str]],
|
|
92
|
+
fields: list[FieldSpec],
|
|
93
|
+
attachments: list[BackupAttachmentSpec],
|
|
94
|
+
) -> BackupRestoreResult:
|
|
95
|
+
"""Restore one host-authorized personal aggregate and receipt in one transaction."""
|
|
96
|
+
# The host supplies verified actor/context and computes the binding fingerprint.
|
|
97
|
+
# No destination owner, access grant, lifecycle or execution flag is accepted.
|
|
98
|
+
try:
|
|
99
|
+
actor_id = str(UUID(actor_id))
|
|
100
|
+
audit_organization_id = str(UUID(audit_organization_id))
|
|
101
|
+
mutation_id = str(UUID(mutation_id))
|
|
102
|
+
if not isinstance(display_name, str) or not display_name:
|
|
103
|
+
raise ValueError("backup display name is required")
|
|
104
|
+
if type(definition_version) is not int or definition_version <= 0:
|
|
105
|
+
raise ValueError("invalid backup definition version")
|
|
106
|
+
_refuse_reserved_generic_definition(definition_key)
|
|
107
|
+
metadata = _metadata_kwargs(
|
|
108
|
+
login_urls=login_urls,
|
|
109
|
+
uri_match_mode=uri_match_mode,
|
|
110
|
+
notes=notes,
|
|
111
|
+
non_secret_fields=non_secret_fields,
|
|
112
|
+
browser_fill_enabled=False,
|
|
113
|
+
)
|
|
114
|
+
# Validation is shared, but restoration must not silently normalize source text.
|
|
115
|
+
metadata["login_urls"] = login_urls
|
|
116
|
+
seen: set[str] = set()
|
|
117
|
+
for field in fields:
|
|
118
|
+
_validate_field_key(field.field_key)
|
|
119
|
+
_refuse_reserved_generic_field(field.field_key)
|
|
120
|
+
if field.field_key in NON_TRANSFERABLE_FIELD_KEYS or field.field_key in seen:
|
|
121
|
+
raise ValueError("backup contains an unsupported or duplicate field")
|
|
122
|
+
if field.handling not in {"visible", "revealable"} or not isinstance(field.value, str):
|
|
123
|
+
raise ValueError("backup field is not portable")
|
|
124
|
+
if field.inject_into_sandbox:
|
|
125
|
+
raise ValueError("backup cannot enable sandbox injection")
|
|
126
|
+
seen.add(field.field_key)
|
|
127
|
+
for attachment in attachments:
|
|
128
|
+
if attachment.source_handling not in {"visible", "revealable"}:
|
|
129
|
+
raise ValueError("backup attachment is not portable")
|
|
130
|
+
_validate_content(attachment.content)
|
|
131
|
+
_validate_text(attachment.label, name="attachment label", maximum=200)
|
|
132
|
+
_validate_text(attachment.file_name, name="attachment filename", maximum=512)
|
|
133
|
+
_validate_text(attachment.media_type, name="attachment media type", maximum=255)
|
|
134
|
+
if attachment.description is not None and len(attachment.description) > 2000:
|
|
135
|
+
raise ValueError("attachment description is too long")
|
|
136
|
+
except (TypeError, ValueError, AttributeError):
|
|
137
|
+
raise ValueError("Invalid backup restore record.") from None
|
|
138
|
+
|
|
139
|
+
async with _restore_transaction():
|
|
140
|
+
receipt, replayed = await reserve_backup_restore_receipt(
|
|
141
|
+
actor_id=actor_id,
|
|
142
|
+
organization_id=audit_organization_id,
|
|
143
|
+
mutation_id=mutation_id,
|
|
144
|
+
request_fingerprint=request_fingerprint,
|
|
145
|
+
)
|
|
146
|
+
if replayed:
|
|
147
|
+
if receipt.completed_at is None or receipt.result_item_id is None:
|
|
148
|
+
raise RuntimeError("backup restore receipt is incomplete")
|
|
149
|
+
await _validate_replay(receipt.result_item_id, actor_id)
|
|
150
|
+
return BackupRestoreResult(receipt.result_item_id, True)
|
|
151
|
+
item = await CredentialItem.create(
|
|
152
|
+
user_id=actor_id,
|
|
153
|
+
organization_id=None,
|
|
154
|
+
definition_key=definition_key,
|
|
155
|
+
definition_version=definition_version,
|
|
156
|
+
provider_key=provider_key,
|
|
157
|
+
display_name=display_name,
|
|
158
|
+
description=description,
|
|
159
|
+
tags=tags,
|
|
160
|
+
status="disabled",
|
|
161
|
+
source="migration",
|
|
162
|
+
access_mode="restricted",
|
|
163
|
+
lifecycle={},
|
|
164
|
+
**metadata,
|
|
165
|
+
created_by=actor_id,
|
|
166
|
+
updated_by=actor_id,
|
|
167
|
+
)
|
|
168
|
+
for field in fields:
|
|
169
|
+
await UserSecret.create(
|
|
170
|
+
user_id=actor_id,
|
|
171
|
+
organization_id=None,
|
|
172
|
+
credential_item_id=item.id,
|
|
173
|
+
field_key=field.field_key,
|
|
174
|
+
key=field.env_key,
|
|
175
|
+
value_encrypted=encrypt_value(field.value),
|
|
176
|
+
value_hint=mask_value(field.value),
|
|
177
|
+
handling=field.handling,
|
|
178
|
+
editable=field.editable,
|
|
179
|
+
is_active=False,
|
|
180
|
+
inject_into_sandbox=False,
|
|
181
|
+
execution_purpose="general",
|
|
182
|
+
description=field.description,
|
|
183
|
+
access_mode="restricted",
|
|
184
|
+
created_by=actor_id,
|
|
185
|
+
updated_by=actor_id,
|
|
186
|
+
)
|
|
187
|
+
for attachment in attachments:
|
|
188
|
+
await CredentialAttachment.create(
|
|
189
|
+
credential_item_id=item.id,
|
|
190
|
+
label=attachment.label,
|
|
191
|
+
description=attachment.description,
|
|
192
|
+
file_name=attachment.file_name,
|
|
193
|
+
media_type=attachment.media_type,
|
|
194
|
+
size_bytes=len(attachment.content),
|
|
195
|
+
value_encrypted=encrypt_bytes(attachment.content),
|
|
196
|
+
handling=attachment.source_handling,
|
|
197
|
+
created_by=actor_id,
|
|
198
|
+
updated_by=actor_id,
|
|
199
|
+
)
|
|
200
|
+
await record_secret_audit(
|
|
201
|
+
action="item_created",
|
|
202
|
+
fail_closed=True,
|
|
203
|
+
actor_id=actor_id,
|
|
204
|
+
organization_id=audit_organization_id,
|
|
205
|
+
user_id=actor_id,
|
|
206
|
+
metadata={
|
|
207
|
+
"credential_item_id": str(item.id),
|
|
208
|
+
"source": "backup_restore",
|
|
209
|
+
"field_count": len(fields),
|
|
210
|
+
"attachment_count": len(attachments),
|
|
211
|
+
"quarantined": True,
|
|
212
|
+
},
|
|
213
|
+
)
|
|
214
|
+
await complete_backup_restore_receipt(receipt.id, result_item_id=str(item.id))
|
|
215
|
+
return BackupRestoreResult(str(item.id), False)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
async def _validate_replay(item_id: str, actor_id: str) -> None:
|
|
219
|
+
rows = (
|
|
220
|
+
await CredentialItem.filter(id=item_id, deleted_at__isnull=True)
|
|
221
|
+
.select_for_update()
|
|
222
|
+
.values(
|
|
223
|
+
"user_id",
|
|
224
|
+
"organization_id",
|
|
225
|
+
"status",
|
|
226
|
+
"access_mode",
|
|
227
|
+
"browser_fill_enabled",
|
|
228
|
+
"definition_key",
|
|
229
|
+
)
|
|
230
|
+
)
|
|
231
|
+
valid = bool(rows)
|
|
232
|
+
if rows:
|
|
233
|
+
item = rows[0]
|
|
234
|
+
valid = (
|
|
235
|
+
str(item["user_id"]) == actor_id
|
|
236
|
+
and item["organization_id"] is None
|
|
237
|
+
and item["status"] == "disabled"
|
|
238
|
+
and item["access_mode"] == "restricted"
|
|
239
|
+
and item["browser_fill_enabled"] is False
|
|
240
|
+
and item["definition_key"] not in {"native_passkey", "passkey_private"}
|
|
241
|
+
)
|
|
242
|
+
fields = await UserSecret.filter(credential_item_id=item_id).values(
|
|
243
|
+
"id",
|
|
244
|
+
"user_id",
|
|
245
|
+
"organization_id",
|
|
246
|
+
"deleted_at",
|
|
247
|
+
"is_active",
|
|
248
|
+
"access_mode",
|
|
249
|
+
"inject_into_sandbox",
|
|
250
|
+
"execution_purpose",
|
|
251
|
+
"handling",
|
|
252
|
+
"field_key",
|
|
253
|
+
)
|
|
254
|
+
for field in fields:
|
|
255
|
+
if field["deleted_at"] is None:
|
|
256
|
+
valid = valid and (
|
|
257
|
+
str(field["user_id"]) == actor_id
|
|
258
|
+
and field["organization_id"] is None
|
|
259
|
+
and field["is_active"] is False
|
|
260
|
+
and field["access_mode"] == "restricted"
|
|
261
|
+
and field["inject_into_sandbox"] is False
|
|
262
|
+
and field["execution_purpose"] == "general"
|
|
263
|
+
and field["handling"] in {"visible", "revealable"}
|
|
264
|
+
and field["field_key"] not in NON_TRANSFERABLE_FIELD_KEYS | {"passkey_private"}
|
|
265
|
+
)
|
|
266
|
+
granted = await UserSecretGrant.filter(credential_item_id=item_id).exists()
|
|
267
|
+
if fields:
|
|
268
|
+
granted = (
|
|
269
|
+
granted
|
|
270
|
+
or await UserSecretGrant.filter(user_secret_id__in=[f["id"] for f in fields]).exists()
|
|
271
|
+
)
|
|
272
|
+
protected = await PasskeyCredential.filter(
|
|
273
|
+
credential_item_id=item_id, deleted_at__isnull=True
|
|
274
|
+
).exists()
|
|
275
|
+
files = await CredentialAttachment.filter(
|
|
276
|
+
credential_item_id=item_id, deleted_at__isnull=True
|
|
277
|
+
).values("handling")
|
|
278
|
+
if (
|
|
279
|
+
not valid
|
|
280
|
+
or granted
|
|
281
|
+
or protected
|
|
282
|
+
or any(f["handling"] not in {"visible", "revealable"} for f in files)
|
|
283
|
+
):
|
|
284
|
+
raise PermissionError(
|
|
285
|
+
"Restored credential has changed; retry cannot confirm its quarantine."
|
|
286
|
+
)
|
|
@@ -15,7 +15,10 @@ from matrx_orm import transaction
|
|
|
15
15
|
|
|
16
16
|
from .model import PACKAGE_DB_NAME, CredentialMutationReceipt
|
|
17
17
|
|
|
18
|
-
MutationOperation = Literal[
|
|
18
|
+
MutationOperation = Literal[
|
|
19
|
+
"create_item", "add_field", "update_field", "native_passkey_create", "native_passkey_import",
|
|
20
|
+
"backup_restore_item",
|
|
21
|
+
]
|
|
19
22
|
PrincipalKind = Literal["user", "organization"]
|
|
20
23
|
|
|
21
24
|
|
|
@@ -242,6 +245,7 @@ async def reserve_native_passkey_receipt(
|
|
|
242
245
|
principal_type: PrincipalKind,
|
|
243
246
|
principal_id: str,
|
|
244
247
|
request_fingerprint: bytes,
|
|
248
|
+
operation: Literal["native_passkey_create", "native_passkey_import"] = "native_passkey_create",
|
|
245
249
|
) -> tuple[MutationReceipt, bool]:
|
|
246
250
|
"""Reserve the native-only receipt shape without exposing generic targets.
|
|
247
251
|
|
|
@@ -255,7 +259,7 @@ async def reserve_native_passkey_receipt(
|
|
|
255
259
|
actor_id=actor_id,
|
|
256
260
|
organization_id=organization_id,
|
|
257
261
|
mutation_id=mutation_id,
|
|
258
|
-
operation=
|
|
262
|
+
operation=operation,
|
|
259
263
|
principal_type=principal_type,
|
|
260
264
|
principal_id=principal_id,
|
|
261
265
|
target_item_id=None,
|
|
@@ -279,3 +283,36 @@ async def complete_native_passkey_receipt(
|
|
|
279
283
|
result_value_version=1,
|
|
280
284
|
result_passkey_id=result_passkey_id,
|
|
281
285
|
)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
async def reserve_backup_restore_receipt(
|
|
289
|
+
*,
|
|
290
|
+
actor_id: str,
|
|
291
|
+
organization_id: str,
|
|
292
|
+
mutation_id: str,
|
|
293
|
+
request_fingerprint: bytes,
|
|
294
|
+
) -> tuple[MutationReceipt, bool]:
|
|
295
|
+
"""Bind a personal restore retry to a server-computed encrypted-envelope digest."""
|
|
296
|
+
if not isinstance(request_fingerprint, bytes) or len(request_fingerprint) != 32:
|
|
297
|
+
raise ValueError("backup restore fingerprint must be 32 bytes")
|
|
298
|
+
return await reserve_mutation_receipt(
|
|
299
|
+
actor_id=actor_id,
|
|
300
|
+
organization_id=organization_id,
|
|
301
|
+
mutation_id=mutation_id,
|
|
302
|
+
operation="backup_restore_item",
|
|
303
|
+
principal_type="user",
|
|
304
|
+
principal_id=actor_id,
|
|
305
|
+
target_item_id=None,
|
|
306
|
+
target_field_id=None,
|
|
307
|
+
request_fingerprint=request_fingerprint,
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
async def complete_backup_restore_receipt(receipt_id: str, *, result_item_id: str) -> None:
|
|
312
|
+
"""Complete the item-only result inside the aggregate restore transaction."""
|
|
313
|
+
await complete_mutation_receipt(
|
|
314
|
+
receipt_id,
|
|
315
|
+
result_item_id=result_item_id,
|
|
316
|
+
result_field_id=None,
|
|
317
|
+
result_value_version=None,
|
|
318
|
+
)
|
|
@@ -200,12 +200,13 @@ async def record_secret_audit(
|
|
|
200
200
|
user_secret_id: str | None = None,
|
|
201
201
|
user_id: str | None = None,
|
|
202
202
|
metadata: dict[str, Any] | None = None,
|
|
203
|
+
fail_closed: bool = False,
|
|
203
204
|
) -> None:
|
|
204
205
|
"""Append one audit row. Records the ACTION and metadata — NEVER a value.
|
|
205
206
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
207
|
+
By default audit failures are logged without failing the operation.
|
|
208
|
+
Security-sensitive aggregate writes pass fail_closed=True so a failed
|
|
209
|
+
audit aborts their outer transaction with a value-free error.
|
|
209
210
|
|
|
210
211
|
``organization_id`` is REQUIRED and refused when empty. ``users
|
|
211
212
|
.user_secret_audit.organization_id`` has been NOT NULL since migration 0444,
|
|
@@ -242,7 +243,9 @@ async def record_secret_audit(
|
|
|
242
243
|
action=action,
|
|
243
244
|
metadata=metadata or {},
|
|
244
245
|
)
|
|
245
|
-
except Exception as exc: # noqa: BLE001 —
|
|
246
|
+
except Exception as exc: # noqa: BLE001 — explicit fail-closed or best-effort policy
|
|
247
|
+
if fail_closed:
|
|
248
|
+
raise RuntimeError("required credential audit failed") from None
|
|
246
249
|
logger.error(
|
|
247
250
|
"secrets audit write FAILED (action=%s secret=%s org=%s): %s",
|
|
248
251
|
action,
|
|
@@ -223,7 +223,17 @@ behavior with structurally-correct one-shot writes.
|
|
|
223
223
|
|
|
224
224
|
## Fallback / replay
|
|
225
225
|
|
|
226
|
-
|
|
226
|
+
For an all-or-nothing copy, construct `Session(recover_individually=False)`
|
|
227
|
+
through an atomic Coordinator. A batch failure then rolls back the whole
|
|
228
|
+
transaction and reports the original cause without individual write recovery;
|
|
229
|
+
the enclosing operation captures the failure. `Session.abort()` discards
|
|
230
|
+
queued rows when the copy body fails before flush.
|
|
231
|
+
|
|
232
|
+
Error-capture rows use the caller's explicit organization. When no organization
|
|
233
|
+
is available, they use the operator SYSTEM organization on their fresh
|
|
234
|
+
connection; a user ID alone cannot supply the missing actor context there.
|
|
235
|
+
|
|
236
|
+
When a regular `Session.flush()` raises, every dropped op is captured to
|
|
227
237
|
`ops.system_write_failure` on a fresh asyncpg connection (bypasses
|
|
228
238
|
the failed transaction's pool slot). The recovery tool
|
|
229
239
|
(`scripts/replay_write_failures.py` in aidream) can replay them
|