matrx-orm 3.1.166__tar.gz → 3.1.168__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.166 → matrx_orm-3.1.168}/PKG-INFO +2 -1
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/create.py +18 -8
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/secrets_battery/FEATURE.md +31 -1
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/secrets_battery/attachments.py +212 -158
- matrx_orm-3.1.168/matrx_orm/secrets_battery/native_source.py +186 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/pyproject.toml +2 -1
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_query_timeout.py +49 -0
- matrx_orm-3.1.168/tests/native_source_cases.py +143 -0
- matrx_orm-3.1.168/tests/native_source_oracle.rs +13 -0
- matrx_orm-3.1.168/tests/test_attachment_mutation_serialization.py +208 -0
- matrx_orm-3.1.168/tests/test_native_source.py +38 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/.gitignore +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/AGENTS.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/BASE_CLASS_METHODS.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/CLAUDE.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/MODEL_API.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/README.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/database/orm/extended/managers/ai_model_base.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/docs/migrations.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/FEATURE.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/MODULE_README.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/__init__.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/adapters/MODULE_README.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/adapters/__init__.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/adapters/async_postgresql.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/adapters/base_adapter.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/adapters/postgrest_client_adapter.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/adapters/supabase_adapter.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/admin/FEATURE.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/admin/__init__.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/admin/router.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/api/__init__.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/api/auth.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/api/config.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/api/handlers.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/api/protocol.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/api/server.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/associations.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/cache_debug.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/catalog.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/catalog_sql.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/client/MODULE_README.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/client/__init__.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/client/postgres_connection.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/client/postgrest.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/client/supabase_auth.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/client/supabase_config.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/client/supabase_manager.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/EXTENDED-TASK.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/FEATURE.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/MODULE_README.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/RELATIONS-TASKS.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/TASKS.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/__init__.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/async_db_manager.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/base.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/config.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/connections.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/db_function.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/diagnostics.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/expressions.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/extended.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/fields.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/introspection.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/listen.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/loop_filters.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/model_dto.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/model_view.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/one_database.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/paginator.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/pool_maintenance.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/pool_watch.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/pooler_role.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/pydantic_bridge.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/registry.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/relations.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/resilience.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/rls_session.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/session_context.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/signals.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/tls.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/transaction.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/types.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/write_queue.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/write_retry.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/entity.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/error_handling.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/exceptions.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/extended/__init__.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/extended/app_error_handler.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/identity_rule.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/local_sqlite.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/middleware/__init__.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/middleware/base.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/migrations/MODULE_README.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/migrations/__init__.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/migrations/cli.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/migrations/ddl.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/migrations/diff.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/migrations/executor.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/migrations/integration.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/migrations/loader.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/migrations/operations.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/migrations/state.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/migrations/table_filter.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/no_org_assignment.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/FEATURE.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/MODULE_README.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/__init__.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/bulk_update_values.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/catalog_select.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/conflict.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/conflict_writes.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/db_functions.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/delete.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/dynamic_admin.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/dynamic_crud.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/graph_walk.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/latest_rows.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/queue_claim.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/read.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/relational_backfill.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/staging_load.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/update.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/platform_access.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/platform_db.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/py.typed +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/pytest_lock_guard.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/pytest_plugin.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/python_sql/MODULE_README.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/python_sql/__init__.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/python_sql/db_objects.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/python_sql/table_detailed_relationships.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/query/FEATURE.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/query/MODULE_README.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/query/__init__.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/query/builder.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/query/executor.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/resources/__init__.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/resources/additional-assigners-2026-09-12.json +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/resources/org_assignment_trigger_baseline.json +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/FEATURE.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/MODULE_README.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/__init__.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/code_handler.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/codegen_writer.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/columns.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/common.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/diff_preview.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/entity_capabilities.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/generator.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/helpers/__init__.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/package_wiring.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/relationships.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/runner.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/schema.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/schema_manager.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/tables.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/views.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/secrets_battery/__init__.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/secrets_battery/crypto.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/secrets_battery/host_authorization.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/secrets_battery/items.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/secrets_battery/model.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/secrets_battery/mutation_receipts.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/secrets_battery/org_service.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/secrets_battery/service.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/README.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/__init__.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/coalesce.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/dag.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/errors.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/fallback.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/flush.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/lifecycle.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/managed.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/op.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/reads.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/session.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/telemetry.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/sql_executor/MODULE_README.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/sql_executor/__init__.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/sql_executor/executor.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/sql_executor/queries.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/sql_executor/registry.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/sql_executor/types.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/sql_executor/utils.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/state.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/utils/__init__.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/utils/sql_utils.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/utils/type_converters.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/write_actor.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/scripts/git-branches.sh +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/scripts/publish.sh +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/scripts/release.sh +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/MODULE_README.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/__init__.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/conftest.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/MODULE_README.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/__init__.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_adapter_registry.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_admin_db_columns.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_admin_response_rendering.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_admin_search_negation.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_admin_search_sentinels.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_advisory_lock.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_agent_message.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_api_auth.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_api_config.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_api_handlers.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_api_protocol.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_array_agg_order_by.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_associations.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_associations_wiring.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_bulk_hydration_offload.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_bulk_update_by_pk.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_bulk_upsert_increment_set_fields.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_cache_debug.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_call_function.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_case_when.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_catalog.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_committed_row_guard.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_composite_pk_filter.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_composite_pk_write_paths.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_config.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_conflict_target.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_conflict_writes.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_connection_codecs.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_connection_poison_guard.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_connection_primitives.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_count_composite_distinct.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_count_limit_param_leak.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_db_function_field.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_ddl_generator.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_deadlock_retry.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_derived_table.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_dirty_tracking.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_dynamic_crud.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_exceptions.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_execute_query_integrity_mapping.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_expression_primitives.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_fields.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_filter_jsonb_agg_array_index.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_from_alias.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_fts.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_func_expression_args.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_graph_walk.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_having_annotate_alias.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_insert_ignore_and_admin_primitives.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_introspect_rls_policies.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_join_and_aggregate.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_latest_rows.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_listen_notify.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_local_sqlite.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_lock_contention_retry.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_managed_write_guard.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_matrx_entity.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_migration_diff_types.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_migration_loader.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_model_instance.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_model_meta.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_optimistic_concurrency.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_order_by_expression.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_org_assignment_inventory.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_orm_banners_are_client_safe.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_parameter_name_hydration.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_patch_jsonb_path.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_persisted_count.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_pool_holder_census.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_pool_loop_recreation.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_pool_maintenance.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_postgrest_filters.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_pydantic_bridge.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_query_builder.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_query_error_detail.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_query_executor_sql.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_querybuilder_clone_semantics.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_queue_claim.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_read_connection_retry.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_registry.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_registry_multi_database.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_registry_multi_schema.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_relational_backfill.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_relations.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_resilience.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_rls_session_scope.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_schema_exists.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_session_advisory_lock.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_standalone_write_session_context.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_state_cache.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_subquery_filter_raw.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_subquery_in_filter.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_subquery_nested_expressions.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_supabase_auth.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_supabase_config.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_tls.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_transaction_database_identity.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_typed_join_projection.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_typed_predicates.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_update_case_expression.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_update_subquery.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_update_with_rebase.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_upsert_default_leak.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_upsert_with_conflict.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_values_jsonb_decode.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_watched_lifecycle.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_write_queue_deadline.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_write_retry_semantics.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/MODULE_README.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/__init__.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/conftest.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/test_bulk_ops.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/test_cache_integration.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/test_crud.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/test_foreign_keys.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/test_m2m.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/test_manager.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/test_migrations_live.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/test_query_execution.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/test_schema_diff.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/verify_committed_row_guard_pg17.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/live_pooler_role.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project/.env.example +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project/README.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project/__init__.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project/generate.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project/generated/.gitkeep +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project/matrx_orm.yaml +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project/test_schema_generation.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project_desktop/.env.example +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project_desktop/README.md +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project_desktop/__init__.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project_desktop/client_example.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project_desktop/client_example.ts +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project_desktop/client_supabase_example.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project_desktop/server.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/entity_tests.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_base_generation.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_composite_pk_fk_generation.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_composite_unique_generation.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_dto_identity_generation.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_entity_capability_generation.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_enum_ordering.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_generate_schema.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_junction_analysis.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_multi_schema_output.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_schema_analysis.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_sql_expression_defaults.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_table_relationships_query.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_view_generation.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema_builder/test_array_default_parsing.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema_builder/test_focused_generation_plan.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema_builder/test_host_models_aggregator.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/__init__.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/conftest.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_cache_no_downgrade.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_capture_poison_proof.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_coalesce.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_dag.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_disk_spill_fallback.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_flush_individual_fallback.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_governed_write.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_managed.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_op_enqueue_site.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_reads.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_record_error_bounded_insert.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_record_error_storm.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_session.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_spill_guard_plugin.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_array_arg_element_type.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_database_name_alias.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_db_functions.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_dynamic_select_unregistered.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_immutable_insert_or_exact_verify.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_keyed_digest.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_model_cls_refactor.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_no_blocking_asyncpg_tls.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_one_database.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_order_by_desc_nulls_last.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_pooler_role_guard.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_secret_audit_names_its_organization.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_secrets_battery.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_secrets_minting_keys.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_shared_lock_guard.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_sql_param_casts.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_sync_twins_guard.py +0 -0
- {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_user_visible_lookup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: matrx-orm
|
|
3
|
-
Version: 3.1.
|
|
3
|
+
Version: 3.1.168
|
|
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
|
|
@@ -23,6 +23,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
23
23
|
Classifier: Typing :: Typed
|
|
24
24
|
Requires-Python: >=3.13
|
|
25
25
|
Requires-Dist: asyncpg>=0.31.0
|
|
26
|
+
Requires-Dist: cbor2<7,>=6.1.3
|
|
26
27
|
Requires-Dist: cryptography>=43.0
|
|
27
28
|
Requires-Dist: gitpython>=3.1.40
|
|
28
29
|
Requires-Dist: matrx-utils>=2.0.50
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
3
4
|
from typing import Any, TYPE_CHECKING, Union
|
|
4
5
|
|
|
5
6
|
from matrx_orm.state import StateManager
|
|
@@ -20,6 +21,19 @@ if TYPE_CHECKING:
|
|
|
20
21
|
OnConflictArg = Union[list[str], "ConflictTarget"]
|
|
21
22
|
|
|
22
23
|
|
|
24
|
+
def _prepare_instance_data(model_cls: type[Model], instance: Model) -> dict[str, Any]:
|
|
25
|
+
"""Coerce a single insert row without occupying the request event loop."""
|
|
26
|
+
data: dict[str, Any] = {}
|
|
27
|
+
for field_name, field in model_cls._fields.items():
|
|
28
|
+
if isinstance(field, Field):
|
|
29
|
+
value = getattr(instance, field_name)
|
|
30
|
+
if value is None and field.default is not None:
|
|
31
|
+
value = field.get_default()
|
|
32
|
+
if value is not None:
|
|
33
|
+
data[field_name] = field.get_db_prep_value(value)
|
|
34
|
+
return data
|
|
35
|
+
|
|
36
|
+
|
|
23
37
|
def _normalise_on_conflict(on_conflict: OnConflictArg) -> tuple[list[str], ConflictTarget | None]:
|
|
24
38
|
"""Internal: unify the two acceptable shapes.
|
|
25
39
|
|
|
@@ -85,14 +99,10 @@ async def save(instance: Model, *, statement_timeout: float | None = None) -> Mo
|
|
|
85
99
|
model_cls = instance.__class__
|
|
86
100
|
await pre_create.send(model_cls, instance=instance)
|
|
87
101
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if value is None and field.default is not None:
|
|
93
|
-
value = field.get_default()
|
|
94
|
-
if value is not None:
|
|
95
|
-
data[field_name] = field.get_db_prep_value(value)
|
|
102
|
+
# JSONB conversion can recursively walk a large OCR/PDF payload and take
|
|
103
|
+
# seconds. This is a shared write boundary, so every single-row create
|
|
104
|
+
# gets the same event-loop protection as the existing bulk write paths.
|
|
105
|
+
data = await asyncio.to_thread(_prepare_instance_data, model_cls, instance)
|
|
96
106
|
|
|
97
107
|
query = QueryBuilder(model_cls)._build_query()
|
|
98
108
|
query["data"] = data
|
|
@@ -57,6 +57,8 @@ fork, and transfer refuse a protected child. Every competing child/lifecycle
|
|
|
57
57
|
mutation locks and re-reads the parent in its transaction; whole-item delete
|
|
58
58
|
remains atomic.
|
|
59
59
|
|
|
60
|
+
**Native source admission (`native_source.py`).** `validate_native_source` is the server reader of the provider's exact source-v1 byte format; Rust remains the encoder and authenticator. It requires an explicit positive source-size limit, preserves nullable names and bytes, rejects duplicate/noncanonical JSON and COSE, and derives public metadata only after maintained P-256 key-pair validation. The result is provider-custody data, never a response model or generic field. `repr` and fixed failures omit material. `scripts/check_native_source_parity.py --rust-core <native-vault-provider/core>` compares the real compiled Rust reader against the server corpus; no mocked oracle. This prerequisite adds no creator, materializer, database row or native readiness. Verified September19 against132 focused tests and525 cross-language cases.
|
|
61
|
+
|
|
60
62
|
**`inject_into_sandbox=true` REQUIRES an env alias — refused otherwise.** A
|
|
61
63
|
container environment is a NAME→value map, so a field with `key IS NULL` has
|
|
62
64
|
nowhere to land and bulk resolution drops it. Accepting the flag anyway meant a
|
|
@@ -94,7 +96,33 @@ refuses sealed bytes; `resolve_credential_attachment` is the trusted in-process
|
|
|
94
96
|
path. Parent delete soft-deletes attachments atomically; fork decrypts and
|
|
95
97
|
re-encrypts independent child rows; transfer needs no child rewrite because
|
|
96
98
|
the parent is the sole ownership authority. Browser-facing list shapes never
|
|
97
|
-
contain ciphertext or plaintext.
|
|
99
|
+
contain ciphertext or plaintext. List serialization is deliberately outside
|
|
100
|
+
the mutation and byte-materialization locking boundary.
|
|
101
|
+
|
|
102
|
+
**Attachment serialization.** Every attachment mutation and byte materialization
|
|
103
|
+
first identifies its parent, then in one `transaction(PACKAGE_DB_NAME)` locks and
|
|
104
|
+
re-reads the live parent before it locks and re-reads the live child. The write
|
|
105
|
+
or byte materialization and any audit use that same transaction. Propagated
|
|
106
|
+
admission or operation errors roll the mutation back; database audit-storage
|
|
107
|
+
errors retain `record_secret_audit`'s existing best-effort savepoint behavior.
|
|
108
|
+
This is the
|
|
109
|
+
same parent-first order as item delete, so delete wins cleanly over an add,
|
|
110
|
+
metadata patch, replacement, child delete, human download, or trusted resolve;
|
|
111
|
+
no child can be created, changed, or materialized under a deleted parent. The
|
|
112
|
+
parent-delete helper deliberately takes no second parent lock because its caller
|
|
113
|
+
already owns it; fork still locks its source parent and copies only its stable
|
|
114
|
+
snapshot.
|
|
115
|
+
|
|
116
|
+
**Live race recipe (requires an explicitly authorized disposable admin item).**
|
|
117
|
+
Through the real Vault router and auth provider, create an admin-owned item and
|
|
118
|
+
pause the attachment request after it takes its parent lock but before its child
|
|
119
|
+
write. Releasing it first must return 201; the following item delete returns
|
|
120
|
+
204 and a fresh router list has no live child. In the inverse ordering, complete
|
|
121
|
+
the delete first and then release the attachment request: it must be refused
|
|
122
|
+
and must not leave a live child. Repeat with download and trusted resolve paused
|
|
123
|
+
before byte materialization. Remove the disposable item through the canonical
|
|
124
|
+
cascade after each run. This is a live concurrency probe, never a replacement
|
|
125
|
+
for the focused unit guards.
|
|
98
126
|
|
|
99
127
|
**Grants: use the single-grant operations.** `add_item_grant` /
|
|
100
128
|
`update_item_grant` / `remove_item_grant` change ONE recipient and leave the
|
|
@@ -328,6 +356,8 @@ delete).
|
|
|
328
356
|
|
|
329
357
|
## Change Log
|
|
330
358
|
|
|
359
|
+
- **2026-09-19** — Added bounded source-v1 server admission reader with cbor2/cryptography and actual Rust differential parity; protected storage and native ceremonies remain separate pending units.
|
|
360
|
+
|
|
331
361
|
- **2026-09-17 (3.1.160)** — **An audit row NAMES its organization, or nothing is
|
|
332
362
|
written.** `record_secret_audit(organization_id=...)` is REQUIRED and refuses an
|
|
333
363
|
empty value by name; it used to default to `None` "for legacy callers", and 51 of
|
|
@@ -12,9 +12,11 @@ from dataclasses import dataclass
|
|
|
12
12
|
from datetime import UTC, datetime
|
|
13
13
|
from typing import Any
|
|
14
14
|
|
|
15
|
+
from matrx_orm import transaction
|
|
16
|
+
|
|
15
17
|
from .crypto import decrypt_bytes, encrypt_bytes
|
|
16
|
-
from .items import CredentialItemNotFoundError, HANDLING_MODES, SealedFieldError
|
|
17
|
-
from .model import CredentialAttachment, CredentialItem
|
|
18
|
+
from .items import CredentialItemNotFoundError, HANDLING_MODES, SealedFieldError, _lock_item_raw
|
|
19
|
+
from .model import PACKAGE_DB_NAME, CredentialAttachment, CredentialItem
|
|
18
20
|
from .org_service import audit_organization, record_secret_audit
|
|
19
21
|
from .service import SecretDecryptError
|
|
20
22
|
|
|
@@ -111,6 +113,20 @@ async def _require_raw(attachment_id: str) -> dict[str, Any]:
|
|
|
111
113
|
return rows[0]
|
|
112
114
|
|
|
113
115
|
|
|
116
|
+
async def _lock_attachment_raw(
|
|
117
|
+
attachment_id: str, *, parent_item_id: str
|
|
118
|
+
) -> dict[str, Any]:
|
|
119
|
+
"""Lock and re-read a live child after its parent is locked."""
|
|
120
|
+
rows = await (
|
|
121
|
+
CredentialAttachment.filter(id=attachment_id, deleted_at__isnull=True)
|
|
122
|
+
.select_for_update()
|
|
123
|
+
.values(*_ATTACHMENT_FIELDS, "value_encrypted")
|
|
124
|
+
)
|
|
125
|
+
if not rows or str(rows[0]["credential_item_id"]) != parent_item_id:
|
|
126
|
+
raise LookupError(f"credential attachment {attachment_id} not found")
|
|
127
|
+
return rows[0]
|
|
128
|
+
|
|
129
|
+
|
|
114
130
|
def _validate_text(value: str, *, name: str, maximum: int) -> str:
|
|
115
131
|
if not isinstance(value, str) or not value.strip():
|
|
116
132
|
raise ValueError(f"{name} is required")
|
|
@@ -177,8 +193,6 @@ async def add_credential_attachment(
|
|
|
177
193
|
audit_organization_id: str | None = None,
|
|
178
194
|
) -> CredentialAttachmentRow:
|
|
179
195
|
"""Encrypt and attach one file to an existing credential item."""
|
|
180
|
-
await _require_item(item_id)
|
|
181
|
-
parent_org = await _parent_item_organization(item_id)
|
|
182
196
|
_validate_content(content)
|
|
183
197
|
_validate_handling(handling)
|
|
184
198
|
clean_label = _validate_text(label, name="label", maximum=200)
|
|
@@ -188,37 +202,44 @@ async def add_credential_attachment(
|
|
|
188
202
|
if description and description.strip()
|
|
189
203
|
else None
|
|
190
204
|
)
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
205
|
+
encrypted_content = encrypt_bytes(content)
|
|
206
|
+
instance_id: str | None = None
|
|
207
|
+
async with transaction(PACKAGE_DB_NAME):
|
|
208
|
+
parent = await _lock_item_raw(item_id)
|
|
209
|
+
instance = await CredentialAttachment.create(
|
|
210
|
+
credential_item_id=item_id,
|
|
211
|
+
label=clean_label,
|
|
212
|
+
description=clean_description,
|
|
213
|
+
file_name=clean_name,
|
|
214
|
+
media_type=_media_type(media_type),
|
|
215
|
+
size_bytes=len(content),
|
|
216
|
+
value_encrypted=encrypted_content,
|
|
217
|
+
handling=handling,
|
|
218
|
+
created_by=actor_id,
|
|
219
|
+
updated_by=actor_id,
|
|
220
|
+
)
|
|
221
|
+
instance_id = str(instance.id)
|
|
222
|
+
await record_secret_audit(
|
|
223
|
+
action="attachment_added",
|
|
224
|
+
actor_id=actor_id,
|
|
225
|
+
organization_id=audit_organization(
|
|
226
|
+
record_organization_id=parent.get("organization_id"),
|
|
227
|
+
request_organization_id=audit_organization_id,
|
|
228
|
+
what=f"add_credential_attachment(item_id={item_id})",
|
|
229
|
+
),
|
|
230
|
+
metadata={
|
|
231
|
+
"credential_item_id": item_id,
|
|
232
|
+
"attachment_id": instance_id,
|
|
233
|
+
"file_name": clean_name,
|
|
234
|
+
"size_bytes": len(content),
|
|
235
|
+
"handling": handling,
|
|
236
|
+
},
|
|
237
|
+
)
|
|
238
|
+
if instance_id is None:
|
|
239
|
+
raise RuntimeError("credential attachment create completed without an id")
|
|
240
|
+
row = await get_credential_attachment(instance_id)
|
|
220
241
|
if row is None:
|
|
221
|
-
raise RuntimeError(f"credential attachment {
|
|
242
|
+
raise RuntimeError(f"credential attachment {instance_id} vanished after create")
|
|
222
243
|
return row
|
|
223
244
|
|
|
224
245
|
|
|
@@ -236,14 +257,8 @@ async def update_credential_attachment_metadata(
|
|
|
236
257
|
audit_organization_id: str | None = None,
|
|
237
258
|
) -> CredentialAttachmentRow:
|
|
238
259
|
"""Patch safe metadata without touching bytes or ``value_version``."""
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
if handling is not None:
|
|
242
|
-
_validate_handling(handling)
|
|
243
|
-
if str(raw["handling"]) == "sealed" and handling != "sealed":
|
|
244
|
-
raise SealedFieldError(
|
|
245
|
-
f"attachment {raw['file_name']!r} is sealed — sealing is a one-way door"
|
|
246
|
-
)
|
|
260
|
+
initial = await _require_raw(attachment_id)
|
|
261
|
+
parent_item_id = str(initial["credential_item_id"])
|
|
247
262
|
updates: dict[str, Any] = {"updated_by": actor_id, "updated_at": datetime.now(UTC)}
|
|
248
263
|
if label is not None:
|
|
249
264
|
updates["label"] = _validate_text(label, name="label", maximum=200)
|
|
@@ -260,21 +275,32 @@ async def update_credential_attachment_metadata(
|
|
|
260
275
|
changed = sorted(set(updates) - {"updated_by", "updated_at"})
|
|
261
276
|
if not changed:
|
|
262
277
|
raise ValueError("no attachment metadata changes supplied")
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
"
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
+
if handling is not None:
|
|
279
|
+
_validate_handling(handling)
|
|
280
|
+
async with transaction(PACKAGE_DB_NAME):
|
|
281
|
+
parent = await _lock_item_raw(parent_item_id)
|
|
282
|
+
raw = await _lock_attachment_raw(attachment_id, parent_item_id=parent_item_id)
|
|
283
|
+
if handling is not None and str(raw["handling"]) == "sealed" and handling != "sealed":
|
|
284
|
+
raise SealedFieldError(
|
|
285
|
+
f"attachment {raw['file_name']!r} is sealed — sealing is a one-way door"
|
|
286
|
+
)
|
|
287
|
+
await CredentialAttachment.update_where(
|
|
288
|
+
{"id": attachment_id, "deleted_at__isnull": True}, **updates
|
|
289
|
+
)
|
|
290
|
+
await record_secret_audit(
|
|
291
|
+
action="attachment_updated",
|
|
292
|
+
actor_id=actor_id,
|
|
293
|
+
organization_id=audit_organization(
|
|
294
|
+
record_organization_id=parent.get("organization_id"),
|
|
295
|
+
request_organization_id=audit_organization_id,
|
|
296
|
+
what=f"update_credential_attachment_metadata(attachment_id={attachment_id})",
|
|
297
|
+
),
|
|
298
|
+
metadata={
|
|
299
|
+
"credential_item_id": parent_item_id,
|
|
300
|
+
"attachment_id": attachment_id,
|
|
301
|
+
"changed": changed,
|
|
302
|
+
},
|
|
303
|
+
)
|
|
278
304
|
row = await get_credential_attachment(attachment_id)
|
|
279
305
|
if row is None:
|
|
280
306
|
raise LookupError(f"credential attachment {attachment_id} not found")
|
|
@@ -291,34 +317,38 @@ async def replace_credential_attachment(
|
|
|
291
317
|
audit_organization_id: str | None = None,
|
|
292
318
|
) -> CredentialAttachmentRow:
|
|
293
319
|
"""Atomically replace the encrypted bytes and file metadata."""
|
|
294
|
-
|
|
295
|
-
|
|
320
|
+
initial = await _require_raw(attachment_id)
|
|
321
|
+
parent_item_id = str(initial["credential_item_id"])
|
|
296
322
|
_validate_content(content)
|
|
297
323
|
clean_name = _validate_text(file_name, name="file_name", maximum=512)
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
324
|
+
encrypted_content = encrypt_bytes(content)
|
|
325
|
+
async with transaction(PACKAGE_DB_NAME):
|
|
326
|
+
parent = await _lock_item_raw(parent_item_id)
|
|
327
|
+
await _lock_attachment_raw(attachment_id, parent_item_id=parent_item_id)
|
|
328
|
+
await CredentialAttachment.update_where(
|
|
329
|
+
{"id": attachment_id, "deleted_at__isnull": True},
|
|
330
|
+
file_name=clean_name,
|
|
331
|
+
media_type=_media_type(media_type),
|
|
332
|
+
size_bytes=len(content),
|
|
333
|
+
value_encrypted=encrypted_content,
|
|
334
|
+
updated_by=actor_id,
|
|
335
|
+
updated_at=datetime.now(UTC),
|
|
336
|
+
)
|
|
337
|
+
await record_secret_audit(
|
|
338
|
+
action="attachment_replaced",
|
|
339
|
+
actor_id=actor_id,
|
|
340
|
+
organization_id=audit_organization(
|
|
341
|
+
record_organization_id=parent.get("organization_id"),
|
|
342
|
+
request_organization_id=audit_organization_id,
|
|
343
|
+
what=f"replace_credential_attachment(attachment_id={attachment_id})",
|
|
344
|
+
),
|
|
345
|
+
metadata={
|
|
346
|
+
"credential_item_id": parent_item_id,
|
|
347
|
+
"attachment_id": attachment_id,
|
|
348
|
+
"file_name": clean_name,
|
|
349
|
+
"size_bytes": len(content),
|
|
350
|
+
},
|
|
351
|
+
)
|
|
322
352
|
row = await get_credential_attachment(attachment_id)
|
|
323
353
|
if row is None:
|
|
324
354
|
raise LookupError(f"credential attachment {attachment_id} not found")
|
|
@@ -331,31 +361,36 @@ async def delete_credential_attachment(
|
|
|
331
361
|
actor_id: str | None = None,
|
|
332
362
|
audit_organization_id: str | None = None,
|
|
333
363
|
) -> bool:
|
|
334
|
-
|
|
335
|
-
|
|
364
|
+
initial = await _require_raw(attachment_id)
|
|
365
|
+
parent_item_id = str(initial["credential_item_id"])
|
|
336
366
|
now = datetime.now(UTC)
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
actor_id=actor_id,
|
|
347
|
-
organization_id=audit_organization(
|
|
348
|
-
record_organization_id=parent_org,
|
|
349
|
-
request_organization_id=audit_organization_id,
|
|
350
|
-
what=f"delete_credential_attachment(attachment_id={attachment_id})",
|
|
351
|
-
),
|
|
352
|
-
metadata={
|
|
353
|
-
"credential_item_id": str(raw["credential_item_id"]),
|
|
354
|
-
"attachment_id": attachment_id,
|
|
355
|
-
"file_name": str(raw["file_name"]),
|
|
356
|
-
},
|
|
367
|
+
deleted = False
|
|
368
|
+
async with transaction(PACKAGE_DB_NAME):
|
|
369
|
+
parent = await _lock_item_raw(parent_item_id)
|
|
370
|
+
raw = await _lock_attachment_raw(attachment_id, parent_item_id=parent_item_id)
|
|
371
|
+
result = await CredentialAttachment.update_where(
|
|
372
|
+
{"id": attachment_id, "deleted_at__isnull": True},
|
|
373
|
+
deleted_at=now,
|
|
374
|
+
updated_by=actor_id,
|
|
375
|
+
updated_at=now,
|
|
357
376
|
)
|
|
358
|
-
|
|
377
|
+
if result.rows_affected:
|
|
378
|
+
deleted = True
|
|
379
|
+
await record_secret_audit(
|
|
380
|
+
action="attachment_deleted",
|
|
381
|
+
actor_id=actor_id,
|
|
382
|
+
organization_id=audit_organization(
|
|
383
|
+
record_organization_id=parent.get("organization_id"),
|
|
384
|
+
request_organization_id=audit_organization_id,
|
|
385
|
+
what=f"delete_credential_attachment(attachment_id={attachment_id})",
|
|
386
|
+
),
|
|
387
|
+
metadata={
|
|
388
|
+
"credential_item_id": parent_item_id,
|
|
389
|
+
"attachment_id": attachment_id,
|
|
390
|
+
"file_name": str(raw["file_name"]),
|
|
391
|
+
},
|
|
392
|
+
)
|
|
393
|
+
return deleted
|
|
359
394
|
|
|
360
395
|
|
|
361
396
|
async def reveal_credential_attachment(
|
|
@@ -365,64 +400,83 @@ async def reveal_credential_attachment(
|
|
|
365
400
|
audit_organization_id: str | None = None,
|
|
366
401
|
) -> tuple[CredentialAttachmentRow, bytes]:
|
|
367
402
|
"""Human download path. Sealed bytes are structurally refused."""
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
403
|
+
initial = await _require_raw(attachment_id)
|
|
404
|
+
parent_item_id = str(initial["credential_item_id"])
|
|
405
|
+
denied_error: SealedFieldError | None = None
|
|
406
|
+
result: tuple[CredentialAttachmentRow, bytes] | None = None
|
|
407
|
+
async with transaction(PACKAGE_DB_NAME):
|
|
408
|
+
parent = await _lock_item_raw(parent_item_id)
|
|
409
|
+
raw = await _lock_attachment_raw(attachment_id, parent_item_id=parent_item_id)
|
|
410
|
+
if str(raw["handling"]) == "sealed":
|
|
411
|
+
await record_secret_audit(
|
|
412
|
+
action="attachment_download_denied",
|
|
413
|
+
actor_id=actor_id,
|
|
414
|
+
organization_id=audit_organization(
|
|
415
|
+
record_organization_id=parent.get("organization_id"),
|
|
416
|
+
request_organization_id=audit_organization_id,
|
|
417
|
+
what=f"reveal_credential_attachment(attachment_id={attachment_id})",
|
|
418
|
+
),
|
|
419
|
+
metadata={
|
|
420
|
+
"credential_item_id": parent_item_id,
|
|
421
|
+
"attachment_id": attachment_id,
|
|
422
|
+
"reason": "sealed",
|
|
423
|
+
},
|
|
424
|
+
)
|
|
425
|
+
denied_error = SealedFieldError(
|
|
426
|
+
f"attachment {raw['file_name']!r} is sealed — no human-facing API may return it"
|
|
427
|
+
)
|
|
428
|
+
else:
|
|
429
|
+
try:
|
|
430
|
+
content = decrypt_bytes(raw["value_encrypted"])
|
|
431
|
+
except Exception as exc:
|
|
432
|
+
raise SecretDecryptError(
|
|
433
|
+
f"credential attachment {attachment_id} does not decrypt with the configured key"
|
|
434
|
+
) from exc
|
|
435
|
+
await CredentialAttachment.update_where(
|
|
436
|
+
{"id": attachment_id, "deleted_at__isnull": True}, last_used_at=datetime.now(UTC)
|
|
437
|
+
)
|
|
438
|
+
await record_secret_audit(
|
|
439
|
+
action="attachment_downloaded",
|
|
440
|
+
actor_id=actor_id,
|
|
441
|
+
organization_id=audit_organization(
|
|
442
|
+
record_organization_id=parent.get("organization_id"),
|
|
443
|
+
request_organization_id=audit_organization_id,
|
|
444
|
+
what=f"reveal_credential_attachment(attachment_id={attachment_id})",
|
|
445
|
+
),
|
|
446
|
+
metadata={
|
|
447
|
+
"credential_item_id": parent_item_id,
|
|
448
|
+
"attachment_id": attachment_id,
|
|
449
|
+
"file_name": str(raw["file_name"]),
|
|
450
|
+
"size_bytes": int(raw["size_bytes"]),
|
|
451
|
+
},
|
|
452
|
+
)
|
|
453
|
+
result = (_row(raw), content)
|
|
454
|
+
if denied_error is not None:
|
|
455
|
+
raise denied_error
|
|
456
|
+
if result is None:
|
|
457
|
+
raise RuntimeError("attachment download completed without a result")
|
|
458
|
+
return result
|
|
411
459
|
|
|
412
460
|
|
|
413
461
|
async def resolve_credential_attachment(
|
|
414
462
|
attachment_id: str,
|
|
415
463
|
) -> tuple[CredentialAttachmentRow, bytes]:
|
|
416
464
|
"""Trusted execution path, including sealed attachments."""
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
465
|
+
initial = await _require_raw(attachment_id)
|
|
466
|
+
parent_item_id = str(initial["credential_item_id"])
|
|
467
|
+
async with transaction(PACKAGE_DB_NAME):
|
|
468
|
+
await _lock_item_raw(parent_item_id)
|
|
469
|
+
raw = await _lock_attachment_raw(attachment_id, parent_item_id=parent_item_id)
|
|
470
|
+
try:
|
|
471
|
+
content = decrypt_bytes(raw["value_encrypted"])
|
|
472
|
+
except Exception as exc:
|
|
473
|
+
raise SecretDecryptError(
|
|
474
|
+
f"credential attachment {attachment_id} does not decrypt with the configured key"
|
|
475
|
+
) from exc
|
|
476
|
+
await CredentialAttachment.update_where(
|
|
477
|
+
{"id": attachment_id, "deleted_at__isnull": True}, last_used_at=datetime.now(UTC)
|
|
478
|
+
)
|
|
479
|
+
return _row(raw), content
|
|
426
480
|
|
|
427
481
|
|
|
428
482
|
async def soft_delete_item_attachments(item_id: str, *, actor_id: str | None = None) -> int:
|