From c89614ec816886e9a773da3047e35cb099c6c9ca Mon Sep 17 00:00:00 2001 From: tegwick Date: Sun, 11 Oct 2026 03:29:45 +0200 Subject: [PATCH] Prepare source-bound one-use catalogue maintenance exception Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade --- .../isolated-postgres-receipt.json | 68 +++++ .../owner-executor.md | 41 +++ .../owner-final-postgres-receipt.json | 63 +++++ .../owner-source-boundary.json | 27 ++ .../proposal.json | 39 +++ ...1-catalogue-maintenance-owner-agreement.md | 66 +++++ ...-11-catalogue-maintenance-source-review.md | 102 ++++++++ tests/test_catalogue_bootstrap.py | 106 ++++++++ ...est_catalogue_bootstrap_owner_negatives.py | 52 ++++ tools/catalogue_bootstrap.py | 241 ++++++++++++++++++ tools/run_approved_catalogue_bootstrap.py | 62 +++++ workplans/ADHOC-2026-10-11.md | 19 ++ 12 files changed, 886 insertions(+) create mode 100644 docs/evidence/2026-10-11-one-app-catalogue-executor/isolated-postgres-receipt.json create mode 100644 docs/evidence/2026-10-11-one-app-catalogue-executor/owner-executor.md create mode 100644 docs/evidence/2026-10-11-one-app-catalogue-executor/owner-final-postgres-receipt.json create mode 100644 docs/evidence/2026-10-11-one-app-catalogue-executor/owner-source-boundary.json create mode 100644 docs/evidence/2026-10-11-one-app-catalogue-executor/proposal.json create mode 100644 docs/reviews/2026-10-11-catalogue-maintenance-owner-agreement.md create mode 100644 docs/reviews/2026-10-11-catalogue-maintenance-source-review.md create mode 100644 tests/test_catalogue_bootstrap.py create mode 100644 tests/test_catalogue_bootstrap_owner_negatives.py create mode 100644 tools/catalogue_bootstrap.py create mode 100644 tools/run_approved_catalogue_bootstrap.py diff --git a/docs/evidence/2026-10-11-one-app-catalogue-executor/isolated-postgres-receipt.json b/docs/evidence/2026-10-11-one-app-catalogue-executor/isolated-postgres-receipt.json new file mode 100644 index 0000000..7f79b18 --- /dev/null +++ b/docs/evidence/2026-10-11-one-app-catalogue-executor/isolated-postgres-receipt.json @@ -0,0 +1,68 @@ +{ + "recorded_at": "2026-10-11T01:18:48.808601+00:00", + "scope": "isolated disposable PostgreSQL only; no native DB/credentials", + "postgres_image": "postgres@sha256:b0f9560a2de083e2cc7382e75f808c7381a32852a7ec49117deedb300e552b24", + "python_dependency_image": "forgejo.coulomb.social/coulomb/user-engine@sha256:f239bbf332b8eb87320c86390fddca1dcb4f7c628a7af75e79dee8058d446a7b", + "resources": { + "postgres_memory_bytes": 536870912, + "postgres_cpu_limit": 1, + "test_worker_memory_bytes": 536870912, + "test_worker_cpu_limit": 1, + "postgres_network": "none", + "worker_network": "isolated PostgreSQL container namespace only", + "read_only_source_mounts": true + }, + "final_test_count": 16, + "final_test_exit_code": 0, + "final_test_seconds": 1.73, + "final_test_output": "Ran 16 tests in 1.730s; OK", + "test_opt_in": "USER_ENGINE_ISOLATED_CATALOGUE_PG_TEST=network-none-fixture", + "preparation_history": [ + { + "tests": 11, + "failed_assertions": 5, + "cause": "Second service constructor ran readiness rollback inside transaction, discarded ordinary-denial audit and serialization lock.", + "corrected": true + }, + { + "tests": 11, + "passed": 11 + }, + { + "tests": 15, + "passed": 15 + }, + { + "tests": 16, + "passed": 16 + }, + { + "tests": 16, + "passed": 16, + "note": "Final portable test path and explicit isolated-fixture opt-in verified." + } + ], + "meaningful_gates": [ + "genuine denial and explicit maintenance audit/event attribution", + "full transaction rollback after first-row failure", + "dry-run no durable entities/audits/events/grant", + "native clock expiration despite stale caller time", + "fresh connection native single-use replay refusal", + "partial/conflicting record refusal", + "exact existing no-change consumes without recovery", + "unavailable or unexpected ordinary allow refusal", + "source/closed/expired grant refusal", + "exact consumed grant digest recovery", + "created-only rollback retains history/deadletters unsent event", + "claimed/delivered event stops", + "changed metadata and later dependency stop", + "existing delivery lock blocks recovery without deletion" + ], + "resources_removed": { + "test_worker_auto_removed": true, + "postgres_container_removed": true, + "private_volume_removed": true + }, + "native_invocation": false, + "founder_approval": null +} diff --git a/docs/evidence/2026-10-11-one-app-catalogue-executor/owner-executor.md b/docs/evidence/2026-10-11-one-app-catalogue-executor/owner-executor.md new file mode 100644 index 0000000..cb331b1 --- /dev/null +++ b/docs/evidence/2026-10-11-one-app-catalogue-executor/owner-executor.md @@ -0,0 +1,41 @@ +# One-app catalogue executor preparation + +This is a separately shipped supervised maintenance command for the existing R1 proposal. No native write, approval, service endpoint, IAM permission or image update has occurred. The prior readback R2 acceptance does not authorize this exception. The applied historical catalogue proposal remains unchanged. + +The executor checks the ordinary provider using a truthful SERVICE principal, issuer `urn:supervised-maintenance`, with no roles, assurance, groups or human identity. A real identified ordinary PDP deny must be retained; unavailable/uncertain responses and unexpected ordinary allows refuse. Only then does an explicitly labelled, one-request maintenance adapter invoke the unchanged `UserEngineService.register_application` for the exact Application and binding. This intentionally overrides the ordinary admission for this single metadata transaction; it does not represent a normal PDP allow or authenticated human session. + +The supervisor must independently verify the founder's native approval of this exact executor AND transport revision, records, deadline, target and recovery. A mode-0600 exact-hash grant is an out-of-band artifact delivered through the existing supervised SSH/kubectl authority. Its JSON is not authentication. Do not derive operator authority from the earlier readback memo, forge JWT claims, reuse a test grant, or ask for credential values. Grant fields are closed and include the native approval/receipt hash and separate maintenance operator attribution. Its lifetime cannot exceed 2026-10-12T23:59:59Z. + +A SERIALIZABLE native transaction and fixed application advisory lock protect absent/exact-match admission. Native DB clock checks run after acquiring the lock and before commit. The audit primary key persists one-use closure across processes/restarts. Exact preexisting match consumes the grant without creating rows/events and therefore has no undo authority. Both services are constructed before BEGIN because the owner's readiness probe rolls back its connection. A tested early version constructed the second service inside BEGIN; five assertions exposed discarded denial/lock state and this was corrected before the final proof. + +Created-only recovery requires the original consumed grant digest before its expiry and exact unchanged rows, no later dependent records, and the existing delivery-worker advisory lock. A missing, claimed or delivered event stops recovery. The original registration event and audit history are retained; only an unclaimed/undelivered event is dead-lettered. This is not reversal of downstream consumers. If recovery is blocked or expiry passed, stop for a separately reviewed owner reconciliation; do not issue a modified grant to defeat the ledger. + +A native dry-run rolls back all User Engine rows, audits, ledger and outbox changes. It calls the genuine authorization provider, which may independently retain its own decision telemetry; dry-run is not globally side-effect free. Owner review of maintenance attribution, event consumers/dead-letter semantics and the precise conditional close-down relaxation remains required before proposing execution. + +## Exact supervised delivery + +After exact source/transport review and founder disposition, create the private grant artifact outside source control, pin its canonical JSON SHA-256 and independently confirm the native receipt. Freeze a fresh Ready pod UID with the approved immutable image and repeat the existing one-app read-only preflight. An unexpected pod, image, partial/conflicting record or provider state stops. + +Run the reviewed transport using `python3 tools/run_approved_catalogue_bootstrap.py --executor tools/catalogue_bootstrap.py --executor-sha256 --grant-file --grant-sha256 --expected-pod-uid --mode dry-run`. Source and grant travel on stdin; the workload internally reads its existing database configuration and enforcement token file, with raw output suppressed. No script or grant is installed into the runtime filesystem. Native dry-run is part of the exceptional admission proposal and must not be run before that scope is approved. The `apply` invocation changes only the mode after successful dry-run and unchanged frozen preconditions; `rollback` is the same approved original grant subject to the recovery checks. This transport is prepared, syntax checked and not run against the estate. + +After apply, verify exact two codec payloads, one consumed ledger, actual ordinary-denial and maintenance audits, attributed registered event, three-way Access/KeyCape/User facts and admitted catalogue readback. Do not claim a human MFA/allow proof from metadata registration. USER-WP-0026-T03 and USER-WP-0028-T02 remain open until their actual native acceptance conditions pass. + +## Conditional close-down obligation + +If the founder chooses this alternative, record a narrowly bounded exception to the normal authenticated operator/PDP admission for these exact two records. Retain original checks as unmet and link approval, exact executor/transport/grant hashes, ordinary deny, maintenance audit, native transaction outcome and verification. Remove private local grant/temporary execution inputs after use; no endpoint or ongoing permission exists to remove. Preserve consumed native ledger and audit/event history. Review unsent event delivery or reconcile claimed/delivered downstream effects before declaring closure. Rejection, expiry, error or recovery remains an uncompleted native catalogue gate, not a scored success. No active close-down waiver is created by this preparation. + + +## Review refinement: retained events and privilege boundary + +Rollback dead-lettering excludes the event from automatic outbox drain. Existing +privileged `deliver_outbox(event_id=...)` and `replay_outbox` can revive it. The +maintenance exception authorizes neither action and cannot promise permanent +prevention of replay. Retain the exact rollback/event IDs and require separate +owner reconciliation and applicable authority before any later targeted delivery +or replay. Do not erase the event or represent downstream reversal as verified. + +Independent analysis reproduced JSON `true` comparing equal to memo version1; +the revised executor requires an exact integer. Five source negatives pass. +The original16 PostgreSQL checks are retained as prior-source mechanism proof; +the revised source needs its own actual final verification and formal User +Engine review before an exact founder exception is requested. diff --git a/docs/evidence/2026-10-11-one-app-catalogue-executor/owner-final-postgres-receipt.json b/docs/evidence/2026-10-11-one-app-catalogue-executor/owner-final-postgres-receipt.json new file mode 100644 index 0000000..3229d94 --- /dev/null +++ b/docs/evidence/2026-10-11-one-app-catalogue-executor/owner-final-postgres-receipt.json @@ -0,0 +1,63 @@ +{ + "reviewed_manifest_sha256": "0ff545d26a0fd72f06040fe377de3efdcc3bd62cdcb6f653247c8ba72f3f34a7", + "executor_sha256": "b0dbbb9786e927055030acf849a72e9a44e9d3d55c84efdf245e419d381684f3", + "scope": "isolated disposable PostgreSQL only; no estate credentials or native invocation", + "attempts": [ + { + "command": "unittest discover current 21 catalogue tests using installed image package", + "exit_code": 0, + "output": "test_absent_registration_preserves_real_denial_and_explicit_maintenance_attribution (test_catalogue_bootstrap.Tests.test_absent_registration_preserves_real_denial_and_explicit_maintenance_attribution) ... ok\ntest_changed_created_record_stops_rollback (test_catalogue_bootstrap.Tests.test_changed_created_record_stops_rollback) ... ok\ntest_claimed_event_stops_rollback_without_deleting_entities (test_catalogue_bootstrap.Tests.test_claimed_event_stops_rollback_without_deleting_entities) ... ok\ntest_conflicting_or_partial_metadata_refuses_without_mutation (test_catalogue_bootstrap.Tests.test_conflicting_or_partial_metadata_refuses_without_mutation) ... ok\ntest_created_only_rollback_preserves_history_and_deadletters_unsent_event (test_catalogue_bootstrap.Tests.test_created_only_rollback_preserves_history_and_deadletters_unsent_event) ... ok\ntest_delivered_event_stops_recovery_and_retains_entities (test_catalogue_bootstrap.Tests.test_delivered_event_stops_recovery_and_retains_entities) ... ok\ntest_delivery_worker_lock_blocks_recovery_without_partial_delete (test_catalogue_bootstrap.Tests.test_delivery_worker_lock_blocks_recovery_without_partial_delete) ... ok\ntest_dry_run_rolls_back_application_binding_all_audit_and_outbox (test_catalogue_bootstrap.Tests.test_dry_run_rolls_back_application_binding_all_audit_and_outbox) ... ok\ntest_exact_preexisting_match_is_nochange_and_has_no_undo (test_catalogue_bootstrap.Tests.test_exact_preexisting_match_is_nochange_and_has_no_undo) ... ok\ntest_expired_or_extra_or_wrong_source_grant_refuses (test_catalogue_bootstrap.Tests.test_expired_or_extra_or_wrong_source_grant_refuses) ... ok\ntest_grant_is_closed_after_one_use_across_fresh_connection (test_catalogue_bootstrap.Tests.test_grant_is_closed_after_one_use_across_fresh_connection) ... ok\ntest_later_dependent_record_stops_created_only_recovery (test_catalogue_bootstrap.Tests.test_later_dependent_record_stops_created_only_recovery) ... ok\ntest_native_clock_refuses_expired_grant_even_with_stale_caller_clock (test_catalogue_bootstrap.Tests.test_native_clock_refuses_expired_grant_even_with_stale_caller_clock) ... ok\ntest_recovery_requires_original_consumed_grant (test_catalogue_bootstrap.Tests.test_recovery_requires_original_consumed_grant) ... ok\ntest_transaction_failure_after_first_row_leaves_no_partial_data_or_grant (test_catalogue_bootstrap.Tests.test_transaction_failure_after_first_row_leaves_no_partial_data_or_grant) ... ok\ntest_unavailable_or_unexpected_ordinary_allow_cannot_trigger_exception (test_catalogue_bootstrap.Tests.test_unavailable_or_unexpected_ordinary_allow_cannot_trigger_exception) ... ok\ntest_deadline_hard_cap_and_no_timezone_are_refused (test_catalogue_bootstrap_owner_negatives.OwnerNegatives.test_deadline_hard_cap_and_no_timezone_are_refused) ... ok\ntest_exact_request_adapter_cannot_switch_app_tenant_or_identity (test_catalogue_bootstrap_owner_negatives.OwnerNegatives.test_exact_request_adapter_cannot_switch_app_tenant_or_identity) ... ok\ntest_exception_is_single_request_with_no_pdp_decision_id (test_catalogue_bootstrap_owner_negatives.OwnerNegatives.test_exception_is_single_request_with_no_pdp_decision_id) ... ok\ntest_json_boolean_is_not_an_authenticated_memo_revision (test_catalogue_bootstrap_owner_negatives.OwnerNegatives.test_json_boolean_is_not_an_authenticated_memo_revision) ... ok\ntest_other_memo_or_added_scope_cannot_expand_fixed_exception (test_catalogue_bootstrap_owner_negatives.OwnerNegatives.test_other_memo_or_added_scope_cannot_expand_fixed_exception) ... ok\n\n----------------------------------------------------------------------\nRan 21 tests in 1.561s\n\nOK\n" + } + ], + "limits": { + "memory": "512m", + "cpus": "1", + "postgres_network": "none", + "worker_network": "container:fixture" + }, + "images": { + "postgres": "postgres@sha256:b0f9560a2de083e2cc7382e75f808c7381a32852a7ec49117deedb300e552b24", + "worker": "forgejo.coulomb.social/coulomb/user-engine@sha256:f239bbf332b8eb87320c86390fddca1dcb4f7c628a7af75e79dee8058d446a7b" + }, + "cleanup": { + "stop_exit_code": 0, + "container_absent": false, + "private_data_tmpfs": true, + "worker_auto_removed": true, + "final_confirmation": { + "container_absent": true, + "verified_command": "docker ps -a --filter name=user-owner-review --format names/status returned empty after auto-removal" + } + }, + "current_test_count": 21, + "current_test_seconds": 1.561, + "prior_failed_preparation_retained": { + "original_receipt_sha256": "964cc3755e96330bf6d1ada63410f8adfdb3c45602e0446369d6dddace412405", + "prior_tests": 11, + "prior_failed_assertions": 5, + "cause": "readiness probe rolled back transaction inside second service constructor", + "old_receipt_unmodified": true + }, + "independently_confirmed_installed_and_checkout_source_hashes": [ + { + "path": "src/user_engine/service.py", + "sha256": "1c60b69e08c36eb3f678893a4a945eeeb9c73d5246c6566857cd8c134f75446d" + }, + { + "path": "src/user_engine/adapters/postgres.py", + "sha256": "528d75431d1f3a7e55423ffa55d34722ac343582e513985db74ba3add36e6da6" + }, + { + "path": "src/user_engine/adapters/flex_auth.py", + "sha256": "73d645b7b37904ebfe0df6f7ed397b74b4c47f505cd64a30c39e60717ce71455" + }, + { + "path": "src/user_engine/domain/models.py", + "sha256": "8c9cb8051a930fa05a1657c8b539510588f1788812d68510df55712d2f4df050" + }, + { + "path": "src/user_engine/store_records.py", + "sha256": "3cec4575434e82e29932a3e9cc7659f3dd8fa7e5be81d5690487033796bbfc16" + } + ] +} diff --git a/docs/evidence/2026-10-11-one-app-catalogue-executor/owner-source-boundary.json b/docs/evidence/2026-10-11-one-app-catalogue-executor/owner-source-boundary.json new file mode 100644 index 0000000..a7f6396 --- /dev/null +++ b/docs/evidence/2026-10-11-one-app-catalogue-executor/owner-source-boundary.json @@ -0,0 +1,27 @@ +{ + "reviewed_current_owner_inputs": [ + { + "path": "src/user_engine/service.py", + "sha256": "1c60b69e08c36eb3f678893a4a945eeeb9c73d5246c6566857cd8c134f75446d" + }, + { + "path": "src/user_engine/adapters/postgres.py", + "sha256": "528d75431d1f3a7e55423ffa55d34722ac343582e513985db74ba3add36e6da6" + }, + { + "path": "src/user_engine/adapters/flex_auth.py", + "sha256": "73d645b7b37904ebfe0df6f7ed397b74b4c47f505cd64a30c39e60717ce71455" + }, + { + "path": "src/user_engine/domain/models.py", + "sha256": "8c9cb8051a930fa05a1657c8b539510588f1788812d68510df55712d2f4df050" + }, + { + "path": "src/user_engine/store_records.py", + "sha256": "3cec4575434e82e29932a3e9cc7659f3dd8fa7e5be81d5690487033796bbfc16" + } + ], + "runtime_sources_modified": false, + "transport_native_execution_tested": false, + "note": "Executor tested with actual published runtime dependencies and current owner source mounted read-only; not a native service execution approval." +} diff --git a/docs/evidence/2026-10-11-one-app-catalogue-executor/proposal.json b/docs/evidence/2026-10-11-one-app-catalogue-executor/proposal.json new file mode 100644 index 0000000..90fc1e4 --- /dev/null +++ b/docs/evidence/2026-10-11-one-app-catalogue-executor/proposal.json @@ -0,0 +1,39 @@ +{ + "proposal_id": "USER-CATALOGUE-INFDEC-20261011-R1", + "revision": "executor-preparation-2-owner-findings", + "status": "prepared-not-approved-not-executed", + "historical_proposal_manifest_sha256": "60b185aabbf6a8448c0c3aef57279b6741db1cd1361b8dab48be29df9f14f78e", + "executor_sha256": "b0dbbb9786e927055030acf849a72e9a44e9d3d55c84efdf245e419d381684f3", + "supervisor_transport_sha256": "3e594e0e8561100ae1eeb34d7208cd5b5e387ae56c29689f155aed94219770f3", + "records_sha256": "23577cc4f3ea6c0ca43eb21ca0e3280eb53fb64e29b030dcd31251593f0f3f2f", + "expected_runtime_image": "forgejo.coulomb.social/coulomb/user-engine@sha256:f239bbf332b8eb87320c86390fddca1dcb4f7c628a7af75e79dee8058d446a7b", + "fresh_pod_uid_required": true, + "deadline": "2026-10-12T23:59:59Z", + "authority": "separate founder-approved supervised administrative exception; out-of-band supervisor validates native receipt", + "existing_r2_does_not_approve_catalogue_mutation": true, + "normal_pdp_refusal_required": true, + "catalogue_entities": [ + "applications/informed-decision", + "application_bindings/informed-decision" + ], + "target_tenant": "tenant:platform", + "retained_events": [ + "application.registered" + ], + "new_endpoint": false, + "runtime_image_change": false, + "iam_grants": false, + "credentials_exported": false, + "budget_estimate": { + "cost_eur_upper_bound": 0.1, + "supervised_seconds_estimate": 300, + "application_downtime_expected_seconds": 0 + }, + "risk": "Exactly one administrative catalogue mutation bypasses ordinary application.register PDP admission under an explicit maintenance exception, retaining genuine denial and truthful service attribution. Public browser login/MFA remains unverified; downstream events may prevent automatic rollback.", + "rollback": "Only exact originally created rows; original consumed grant digest; before expiry; no dependencies; delivery lock; refuse any claimed/delivered/missing event; retain audits and dead-letter unsent registered event. No full downstream reversal claim. Dead-lettering stops automatic drain only; existing privileged targeted delivery/replay can revive the retained event. This maintenance grant authorizes neither; any later replay requires separate owner reconciliation/disposition.", + "owner_review_required": [ + "User Engine audit/service attribution and transaction semantics", + "NetKingdom exception scope, catalogue facts, event consumers/dead-letter semantics", + "Custodian precise founder disposition and conditional close-down obligation" + ] +} diff --git a/docs/reviews/2026-10-11-catalogue-maintenance-owner-agreement.md b/docs/reviews/2026-10-11-catalogue-maintenance-owner-agreement.md new file mode 100644 index 0000000..0acf46e --- /dev/null +++ b/docs/reviews/2026-10-11-catalogue-maintenance-owner-agreement.md @@ -0,0 +1,66 @@ +# User Engine owner agreement — exact catalogue maintenance source + +- reviewer: user-engine-owner (agent user-engine, codex) +- decision: approved +- comment: INTENT §Layer Engine/PIP, Binding rules 1/3/5/6 and Product Boundaries; exact nine-file preparation manifest SHA256 0ff545d26a0fd72f06040fe377de3efdcc3bd62cdcb6f653247c8ba72f3f34a7, executor SHA256 b0dbbb9786e927055030acf849a72e9a44e9d3d55c84efdf245e419d381684f3 and transport SHA256 3e594e0e8561100ae1eeb34d7208cd5b5e387ae56c29689f155aed94219770f3. Work item USER-WP-ADHOC-2026-10-11-T03. + +This dedicated worker loaded User Engine AGENTS/INTENT and worked from its +checkout, independently inspecting registration, authorization, domain codecs, +PostgreSQL persistence/transactions, audit and outbox consumers. This entry +reviews User Engine only. Approval is source agreement for the precisely +labelled maintenance alternative; it is no founder decision, native execution +permission, runtime admission, human authentication proof or active relaxation. + +Catalogue application/binding facts belong to this Engine. Ordinary protected +registration still requires access-engine authorization. The standalone tool +truthfully overrides an identified genuine ordinary denial for exactly +informed-decision / OIDC informed-decision-approver / system informed-decision, +with a separate one-use service maintenance actor, empty roles/assurance and no +PDP allow ID. This is an exception to normal admission, requiring a separate +exact founder disposition and independently verified native receipt covering +executor AND transport, frozen Ready pod/image, records, deadline and recovery. +The private hash-pinned artifact asserts this external authority; it does not +authenticate its author. Prior R2 readback authority does not authorize it. + +Strict integer memo revision rejects JSON true. Fixed request/action/actor/tenant +bindings, native database clock checks, serializable transaction and advisory +lock protect exact admission; durable audit key prevents reuse after restart. +Both services are constructed before the transaction because owner readiness +rolls back the connection. The prior constructor defect and five failed +assertions remain in original preparation history. Exact preexisting match +consumes with no undo. Partial/conflicting rows, provider unavailable, uncertain +denial and unexpected ordinary allow refuse. Native dry-run rolls back local +records/audit/outbox/ledger while external provider telemetry may remain. Final +clock check precedes commit; it is not a measured commit-completion deadline. + +Created-only recovery checks the original grant digest and expiry, unchanged +metadata, absent dependencies, original events and the existing delivery-worker +lock. Claimed/delivered/missing events stop recovery; history survives. +Dead-lettering excludes automatic drain only. Ordinary separately authorized +targeted delivery and replay can revive a retained event; this exception grants +neither. Owner reconciliation is required before that later action. No complete +consumer inventory or reversal of downstream effects is asserted. + +The existing HTTP delivery adapter sends the eight-field platform envelope, +including maintenance labels in data. application.registered is attributive, +not an invitation mail class. Source attribution does not prove native consumer +receipt, stream completeness, MFA success or user access. Verify actual bound +rows, consumed ledger, genuine denial/maintenance audit, registered event and +its intended consumer under existing sender admission after any separately +approved execution. USER-WP-0026-T03 and USER-WP-0028-T02 remain open. + +All 21 current checks passed in 1.561s using real disposable PostgreSQL, installed +immutable f239 image package, read-only staged executor/test mount, isolated +network and 512Mi/1CPU per container. Installed service/postgres/flex-auth/codec/ +domain hashes match current owner-source boundary. Both fixture workers and +PostgreSQL were auto-removed; data existed only on tmpfs. Initial immediate +cleanup query raced auto-removal and is retained as false; a later query +confirmed absence. No native database/workload write, login, credential read, +transport dispatch or human decision occurred. The original16 prior-source +receipt is retained unchanged and is supplemented, not relabelled as new proof. + +Before execution, the requesting Custodian must obtain all required independent +owner reviews and the exact separate founder exception, record its conditional +close-down obligation and preserve ordinary authentication/admission as unmet. +Rejection, expiry, recovery failure and unverified execution cannot be scored +as successful native catalogue acceptance. diff --git a/docs/reviews/2026-10-11-catalogue-maintenance-source-review.md b/docs/reviews/2026-10-11-catalogue-maintenance-source-review.md new file mode 100644 index 0000000..72ad4ca --- /dev/null +++ b/docs/reviews/2026-10-11-catalogue-maintenance-source-review.md @@ -0,0 +1,102 @@ +# User Engine catalogue maintenance source review — 2026-10-11 + +Disposition: changes requested to exact preparation manifest +SHA256 8cc315874a58804c9cd6e48c766c26ff08e0b2435a82364100a795bb79ec7a85. This is technical source analysis, not a second formal +owner-role approval, founder decision, native admission or execution proof. +The same worker previously prepared NetKingdom architectural review, so a +fresh dedicated user-engine-owner worker (or founder fallback) must provide +any formal review entry required by delegated-owner-review v0.1. All seven +staged file digests were verified; applied User Engine HEAD is +1eefbfc44a06458ae3cedaf1e79186384c5f558c. Read current AGENTS/INTENT, DTO codec, +service authorization/registration/audit/outbox and PostgreSQL transaction/locks. + +## Required narrow corrections + +1. Grant validator compares memo_version by Python equality, so JSON true is + accepted as integer1. Original source acceptance was reproduced without a + database. The staged correction requires `type(memo_version) is int` before + exact equality. Corrected executor SHA256: b0dbbb9786e927055030acf849a72e9a44e9d3d55c84efdf245e419d381684f3. Refresh proposal + executor hash and freeze the resulting final packet before human review; + the original seven-file manifest is not a review of those changed bytes. +2. Recovery dead-letters an unclaimed/undelivered registered event and preserves + history. That excludes automatic pending drain. It does **not** permanently + prohibit later privileged delivery: existing `deliver_outbox(event_id=...)` + bypasses the dead-letter skip, and `replay_outbox` can clear dead_lettered_at + after ordinary outbox.replay authorization. Execution/recovery documentation + must therefore explicitly require owner reconciliation before such later + targeted delivery/replay of a rolled-back registration. Do not describe + dead-letter as preventing all downstream observation, or change normal + runtime policy to evade this review. Do not authorize targeted replay as part + of the one-app maintenance grant. Retain event IDs and the rollback ledger + for the reconciliation boundary. + +## Responsibility and authority basis + +INTENT's Engine/PIP/PEP boundary makes catalogue facts owned by User Engine and +ordinary protected registration fail closed through access-engine. This tool +intentionally invokes an exception adapter after a genuine ordinary denial; +it does **not** comply with normal admission and must be explicitly approved +as a narrow maintenance exception. Source calls it that truthfully, issues no +PDP decision ID and retains the genuine denial. It changes no installed HTTP +endpoint, runtime package, normal authorization adapter or IAM policy. + +Maintenance actor is SERVICE, issuer urn:supervised-maintenance and explicit +operator attribution, with empty audience/roles/assurance/groups. It is not a +verified human or invented IAM login. A mode0600 hash-pinned JSON file is not +authentication of its author or proof of the native human receipt. Independent +supervisor verification of exact founder disposition, executor AND transport, +records, target, deadline and recovery is mandatory before any native dry-run +or apply; R2 readback approval does not supply it. No active relaxation is +created by preparation. A future relaxation requires the precise conditional +security-close-down entry, and must preserve unmet normal admission evidence. + +## Transaction and persistence observations + +Both services are created before BEGIN: readiness invokes rollback, so moving +that constructor into the transaction would silently discard denial and lock. +SERIALIZABLE transaction plus fixed application advisory lock and native DB +clock checks bound the exact two DTOs. Durable audit primary key persists the +one-use ledger across processes; exact preexisting rows consume once with no +creation/undo. Half-existing or conflicting rows refuse. Hash-bound grant, +fixed memo/record/source and maximum deadline are checked, with no schema +migration. Final clock check precedes driver commit; it is a precommit check, +not a claim PostgreSQL commit completion is observed before the deadline. + +Exception adapter permits only the fixed action/resource/app/tenant/actor and +correlation, one check with <=30-second lifetime. Independent negatives verify +wrong app, tenant, actor, action or correlation refusal, repeated invocation +refusal and no PDP decision ID. Native transaction clocks and persistent ledger +are not established by those pure tests; original receipt reports sixteen +isolated PostgreSQL tests only, with the earlier constructor defect preserved +as failed preparation history. No estate execution is inferred. + +Created-only rollback checks original consumed grant hash and expiry, unchanged +DTOs, absence of later dependent records, corresponding worker advisory locks, +and absence of missing/claimed/delivered events before compare-delete. Audits +and outbox records remain. Actual privileged replay caveat above must be retained. +Downstream effects cannot be undone by this command: claimed/delivered event, +changed metadata, dependency or expired grant stops for separate owner recovery. + +## Event consumers and retained evidence + +`application.registered` is attributive in User Engine evidence classification. +Current HTTPOutboxDeliveryAdapter emits the existing eight-field event envelope +and forwards maintenance_exception/proposal_id/approval_id/operator_id in data. +It routes only family invitation classes to transactional mail; this registration +is not a mail event. Its standard transport is audit-core. No consumer acceptance, +complete stream inventory or human MFA outcome is established by this source +review. After apply verify actual allowed event envelope and intended consumer +receipt under existing sender admission, without assuming audit arrival proves +source completeness or inferring grants from the registered app fact. + +## Independent source-only verification + +Five additional meaningful tests pass on the corrected executor: strict numeric +memo revision; other memo/extra scope refusal; hard deadline and timezone refusal; +fixed request subject/app/tenant/action/correlation refusal; one-check lifetime +binding without PDP ID. Original executor accepts the boolean counterexample. +Run with PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=/home/worsch/user-engine/src +python3 -m unittest discover -s /tmp/identity-catalogue-review-stage/user-engine/tests -v. +No database, credential, runtime login, native writes or transport dispatch occurred. +These five tests are separate from the original sixteen PostgreSQL tests; no +re-run of modified-source PostgreSQL tests is claimed. diff --git a/tests/test_catalogue_bootstrap.py b/tests/test_catalogue_bootstrap.py new file mode 100644 index 0000000..1c423a4 --- /dev/null +++ b/tests/test_catalogue_bootstrap.py @@ -0,0 +1,106 @@ +"""Real isolated PostgreSQL tests; no native estate connection/identity claims.""" +import importlib.util +from pathlib import Path +import unittest,json,uuid,os,hashlib +from datetime import datetime,timedelta,timezone +from dataclasses import replace +import psycopg +from user_engine.adapters.postgres import PostgresUserEngineStore +from user_engine.domain import AuthorizationDecision,AuthorizationEffect,Application +spec=importlib.util.spec_from_file_location('catalogue_bootstrap',Path(__file__).resolve().parents[1]/'tools'/'catalogue_bootstrap.py') +m=importlib.util.module_from_spec(spec);spec.loader.exec_module(m) +class Deny: + def check(self,request):return AuthorizationDecision(AuthorizationEffect.DENY,decision_id='fixture-pdp-denial',reason='no_matching_role_or_context') +class Unavailable: + def check(self,request):return AuthorizationDecision(AuthorizationEffect.DENY,decision_id=None,reason='authorization service unavailable') +class Allow: + def check(self,request):return AuthorizationDecision.for_standalone(AuthorizationEffect.ALLOW,reason='fixture-ordinary-allow') +@unittest.skipUnless(os.environ.get('USER_ENGINE_ISOLATED_CATALOGUE_PG_TEST')=='network-none-fixture', 'requires explicitly isolated disposable PostgreSQL fixture; never uses runtime credentials') +class Tests(unittest.TestCase): + def setUp(self): + self.conn=psycopg.connect('host=127.0.0.1 user=postgres dbname=postgres') + with self.conn.cursor() as c:c.execute('DROP SCHEMA public CASCADE');c.execute('CREATE SCHEMA public') + self.conn.commit();self.store=PostgresUserEngineStore(self.conn);self.store.migrate() + self.g={'schema':m.SCHEMA,'proposal_id':m.PROPOSAL,'approval_id':str(uuid.uuid4()),'memo_id':'infd-20261011-user-catalogue-bootstrap-r1','memo_version':1,'approval_receipt_sha256':'a'*64,'executor_sha256':'b'*64,'records_sha256':m.digest(m.records()),'expires_at':(datetime.now(timezone.utc)+timedelta(minutes=10)).isoformat(),'operator_id':'fixture-supervised-maintenance','authority':'founder-approved-one-time-administrative-exception'} + def tearDown(self):self.conn.close() + def run_op(self,mode='apply',auth=None,grant=None): + g=grant or self.g + return m.execute(self.conn,auth or Deny(),g,expected_grant_sha256=m.digest(g),actual_executor_sha256='b'*64,mode=mode) + def counts(self): + with self.conn.cursor() as c: + counts=[] + for table in ['user_engine_records','user_engine_audit_records','user_engine_outbox_events']:c.execute('SELECT count(*) FROM '+table);counts.append(c.fetchone()[0]) + self.conn.rollback();return counts + def test_absent_registration_preserves_real_denial_and_explicit_maintenance_attribution(self): + result=self.run_op();self.assertEqual(result['result'],'created');self.assertEqual(self.counts(),[2,3,1]);self.assertFalse(result['ordinary_pdp_allow_claimed']) + audits=self.store.audit_log();self.assertTrue(any(x.summary=='authorization denied' and x.decision_id=='fixture-pdp-denial' for x in audits));self.assertTrue(all(x.actor.issuer=='urn:supervised-maintenance' and not x.actor.roles and not x.actor.assurance for x in audits));event=self.store.outbox_history()[0];self.assertTrue(event.payload['maintenance_exception']);self.assertEqual(event.payload['approval_id'],self.g['approval_id']) + def test_dry_run_rolls_back_application_binding_all_audit_and_outbox(self): + result=self.run_op('dry-run');self.assertTrue(result['dry_run_rolled_back']);self.assertEqual(self.counts(),[0,0,0]);self.assertEqual(self.run_op()['result'],'created') + def test_grant_is_closed_after_one_use_across_fresh_connection(self): + self.run_op();self.conn.close();self.conn=psycopg.connect('host=127.0.0.1 user=postgres dbname=postgres') + with self.assertRaises(m.Refused):self.run_op() + self.assertEqual(self.counts(),[2,3,1]) + def test_exact_preexisting_match_is_nochange_and_has_no_undo(self): + with self.store.transaction():self.store.save_application(m.APP);self.store.save_binding(m.BINDING) + self.assertEqual(self.run_op()['result'],'no-change');self.assertEqual(self.counts(),[2,1,0]) + with self.assertRaises(m.Refused):self.run_op('rollback') + def test_conflicting_or_partial_metadata_refuses_without_mutation(self): + with self.store.transaction():self.store.save_application(Application('informed-decision','Other','someone')) + with self.assertRaises(m.Refused):self.run_op() + self.assertEqual(self.counts(),[1,0,0]) + def test_unavailable_or_unexpected_ordinary_allow_cannot_trigger_exception(self): + for auth in [Unavailable(),Allow()]: + with self.assertRaises(m.Refused):self.run_op(auth=auth) + self.assertEqual(self.counts(),[0,0,0]) + def test_expired_or_extra_or_wrong_source_grant_refuses(self): + for g in [{**self.g,'expires_at':'2026-01-01T00:00:00Z'},{**self.g,'extra':'grant'},{**self.g,'executor_sha256':'c'*64}]: + with self.assertRaises(m.Refused):self.run_op(grant=g) + self.assertEqual(self.counts(),[0,0,0]) + def test_created_only_rollback_preserves_history_and_deadletters_unsent_event(self): + self.run_op();result=self.run_op('rollback');self.assertEqual(result['result'],'rolled-back');self.assertEqual(self.counts(),[0,4,1]);self.assertEqual(self.store.pending_outbox(),());self.assertIsNotNone(self.store.outbox_history()[0].dead_lettered_at) + with self.assertRaises(m.Refused):self.run_op('rollback') + def test_claimed_event_stops_rollback_without_deleting_entities(self): + self.run_op();event=self.store.outbox_history()[0] + with self.store.transaction():self.store.save_outbox(replace(event,claimed_by='other-worker',claimed_at=datetime.now(timezone.utc))) + with self.assertRaises(m.Refused):self.run_op('rollback') + self.assertEqual(self.counts(),[2,3,1]) + def test_changed_created_record_stops_rollback(self): + self.run_op() + with self.store.transaction():self.store.save_application(replace(m.APP,owner='new-owner')) + with self.assertRaises(m.Refused):self.run_op('rollback') + self.assertEqual(self.counts(),[2,3,1]) + def test_transaction_failure_after_first_row_leaves_no_partial_data_or_grant(self): + with self.conn.cursor() as c:c.execute("CREATE FUNCTION reject_binding() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN IF NEW.record_type = 'application_bindings' THEN RAISE EXCEPTION 'test interruption'; END IF; RETURN NEW; END $$");c.execute('CREATE TRIGGER deny_binding BEFORE INSERT ON user_engine_records FOR EACH ROW EXECUTE FUNCTION reject_binding()') + self.conn.commit() + with self.assertRaises(Exception):self.run_op() + self.assertEqual(self.counts(),[0,0,0]) + def test_delivered_event_stops_recovery_and_retains_entities(self): + self.run_op();event=self.store.outbox_history()[0] + with self.store.transaction():self.store.save_outbox(replace(event,delivered_at=datetime.now(timezone.utc))) + with self.assertRaises(m.Refused):self.run_op('rollback') + self.assertEqual(self.counts(),[2,3,1]) + def test_recovery_requires_original_consumed_grant(self): + self.run_op() + with self.assertRaises(m.Refused):self.run_op('rollback',grant={**self.g,'operator_id':'different-maintenance-operator'}) + self.assertEqual(self.counts(),[2,3,1]) + def test_native_clock_refuses_expired_grant_even_with_stale_caller_clock(self): + g={**self.g,'expires_at':(datetime.now(timezone.utc)-timedelta(seconds=1)).isoformat()} + with self.assertRaises(m.Refused):m.execute(self.conn,Deny(),g,expected_grant_sha256=m.digest(g),actual_executor_sha256='b'*64,mode='apply',now=datetime.now(timezone.utc)-timedelta(minutes=1)) + self.assertEqual(self.counts(),[0,0,0]) + def test_delivery_worker_lock_blocks_recovery_without_partial_delete(self): + self.run_op();event=self.store.outbox_history()[0] + other=psycopg.connect('host=127.0.0.1 user=postgres dbname=postgres') + try: + with other.cursor() as c:c.execute('SELECT pg_advisory_lock(hashtextextended(%s,0))',('user-engine:tenant-lifecycle:outbox-event:'+event.event_id,)) + other.commit() + self.conn.close();self.conn=psycopg.connect('host=127.0.0.1 user=postgres dbname=postgres',options='-c lock_timeout=100ms') + with self.assertRaises(psycopg.errors.LockNotAvailable):self.run_op('rollback') + self.assertEqual(self.counts(),[2,3,1]) + finally:other.close() + def test_later_dependent_record_stops_created_only_recovery(self): + self.run_op() + with self.conn.cursor() as c:c.execute("INSERT INTO user_engine_records (record_type,record_key,application_id,payload) VALUES ('fixture_dependencies','fixture-key','informed-decision','{}'::jsonb)") + self.conn.commit() + with self.assertRaises(m.Refused):self.run_op('rollback') + self.assertEqual(self.counts(),[3,3,1]) +if __name__=='__main__':unittest.main() diff --git a/tests/test_catalogue_bootstrap_owner_negatives.py b/tests/test_catalogue_bootstrap_owner_negatives.py new file mode 100644 index 0000000..ef08dcc --- /dev/null +++ b/tests/test_catalogue_bootstrap_owner_negatives.py @@ -0,0 +1,52 @@ +"""Independent source-only negatives; no database/runtime/approval claims.""" +import importlib.util +from pathlib import Path +from dataclasses import replace +from datetime import datetime, timedelta, timezone +import unittest +from user_engine.domain import AuthorizationRequest +spec = importlib.util.spec_from_file_location("review_catalogue_executor", Path(__file__).resolve().parents[1] / "tools/catalogue_bootstrap.py") +m = importlib.util.module_from_spec(spec) +spec.loader.exec_module(m) +NOW = datetime(2026, 10, 11, 12, tzinfo=timezone.utc) + +def grant(): + return {"schema": m.SCHEMA, "proposal_id": m.PROPOSAL, + "approval_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", + "memo_id": "infd-20261011-user-catalogue-bootstrap-r1", "memo_version": 1, + "approval_receipt_sha256": "a" * 64, "executor_sha256": "b" * 64, + "records_sha256": m.digest(m.records()), "expires_at": "2026-10-12T12:00:00Z", + "operator_id": "fixture-maintenance-service", "authority": "founder-approved-one-time-administrative-exception"} + +class OwnerNegatives(unittest.TestCase): + def validate(self, value): + return m.validate_grant(value, expected_grant_sha256=m.digest(value), actual_executor_sha256="b"*64, now=NOW) + + def test_json_boolean_is_not_an_authenticated_memo_revision(self): + with self.assertRaises(m.Refused): self.validate({**grant(), "memo_version": True}) + + def test_other_memo_or_added_scope_cannot_expand_fixed_exception(self): + for value in ({**grant(), "memo_id": "another-approved-memo"}, {**grant(), "allowed_apps": ["other"]}): + with self.subTest(value=value), self.assertRaises(m.Refused): self.validate(value) + + def test_deadline_hard_cap_and_no_timezone_are_refused(self): + for expires in ("2026-10-13T00:00:00Z", "2026-10-12T12:00:00"): + with self.subTest(expires=expires), self.assertRaises(m.Refused): self.validate({**grant(), "expires_at": expires}) + + def test_exact_request_adapter_cannot_switch_app_tenant_or_identity(self): + actor=m._actor(grant()) + request=AuthorizationRequest(actor, "user-engine:application", "informed-decision", "application.register", "tenant:platform", "fixture:correlation", "informed-decision") + changes=[{"resource_id":"other"}, {"application_id":"other"}, {"tenant":"tenant:other"}, {"actor":replace(actor,subject="different-maintenance")}, {"action":"application.delete"}, {"correlation_id":"other:correlation"}] + for fields in changes: + adapter=m._ExceptionAuthorization(actor,"fixture:correlation",datetime.now(timezone.utc)+timedelta(minutes=1)) + with self.subTest(fields=fields), self.assertRaises(m.Refused): adapter.check(replace(request,**fields)) + + def test_exception_is_single_request_with_no_pdp_decision_id(self): + actor=m._actor(grant());adapter=m._ExceptionAuthorization(actor,"fixture:correlation",datetime.now(timezone.utc)+timedelta(minutes=1)) + request=AuthorizationRequest(actor,"user-engine:application","informed-decision","application.register","tenant:platform","fixture:correlation","informed-decision") + decision=adapter.check(request) + self.assertTrue(decision.allowed);self.assertIsNone(decision.decision_id) + self.assertEqual(decision.binding,"supervised-maintenance") + with self.assertRaises(m.Refused): adapter.check(request) + +if __name__ == "__main__": unittest.main() diff --git a/tools/catalogue_bootstrap.py b/tools/catalogue_bootstrap.py new file mode 100644 index 0000000..b6250ca --- /dev/null +++ b/tools/catalogue_bootstrap.py @@ -0,0 +1,241 @@ +"""One-time supervised catalogue maintenance; no installed runtime endpoint. + +A private, exact-hash supervisor artifact is an out-of-band maintenance grant, +not authentication of its JSON author. The operator must independently confirm +the founder's native disposition before supplying it. No JWT role is invented. +""" +from __future__ import annotations +import argparse +from dataclasses import replace +from datetime import datetime, timedelta, timezone +import hashlib +import json +import os +from pathlib import Path +import re +import stat + +from user_engine.adapters.postgres import PostgresUserEngineStore +from user_engine.domain import (Actor, Application, ApplicationBinding, AuditRecord, + AuthorizationDecision, AuthorizationEffect, PrincipalType) +from user_engine.service import UserEngineService, AuthorizationDenied +from user_engine.store_records import store_record_for + +APP = Application('informed-decision', 'Informed Decision', 'informed-decision') +BINDING = ApplicationBinding('informed-decision', oidc_client_id='informed-decision-approver', protected_system_id='informed-decision') +PROPOSAL = 'USER-CATALOGUE-INFDEC-20261011-R1' +SCHEMA = 'user-engine.supervised-catalogue-maintenance.v1' +KEYS = {'schema','proposal_id','approval_id','memo_id','memo_version','approval_receipt_sha256','executor_sha256','records_sha256','expires_at','operator_id','authority'} + +def digest(value): + return hashlib.sha256(json.dumps(value, sort_keys=True, separators=(',', ':')).encode()).hexdigest() + +def records(): + return [store_record_for(x).__dict__ for x in (APP, BINDING)] + +class Refused(ValueError): + pass + +def validate_grant(grant, *, expected_grant_sha256, actual_executor_sha256, now): + if (type(grant) is not dict or set(grant) != KEYS + or grant['schema'] != SCHEMA or grant['proposal_id'] != PROPOSAL + or grant['authority'] != 'founder-approved-one-time-administrative-exception' + or grant['memo_id'] != 'infd-20261011-user-catalogue-bootstrap-r1' + or type(grant['memo_version']) is not int or grant['memo_version'] != 1 + or not re.fullmatch(r'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}', grant['approval_id']) + or not re.fullmatch(r'[a-zA-Z0-9:._-]{3,80}', grant['operator_id'])): + raise Refused('closed grant required') + for key in ('approval_receipt_sha256', 'executor_sha256', 'records_sha256'): + if not re.fullmatch(r'[0-9a-f]{64}', grant[key]): + raise Refused('exact evidence digests required') + expires = datetime.fromisoformat(grant['expires_at'].replace('Z', '+00:00')) + if (expires.tzinfo is None or now.tzinfo is None or now >= expires + or expires > datetime(2026, 10, 12, 23, 59, 59, tzinfo=timezone.utc)): + raise Refused('expired grant') + if (digest(grant) != expected_grant_sha256 or grant['executor_sha256'] != actual_executor_sha256 + or grant['records_sha256'] != digest(records())): + raise Refused('approved source differs') + return expires + +def _summary(cursor, audit_id): + cursor.execute('SELECT summary FROM user_engine_audit_records WHERE audit_id = %s', (audit_id,)) + row = cursor.fetchone() + return None if row is None else json.loads(row[0]) + +def _event_ids(cursor, correlation): + cursor.execute('SELECT event_id FROM user_engine_outbox_events WHERE correlation_id = %s ORDER BY event_id', (correlation,)) + return [r[0] for r in cursor.fetchall()] + +def _actor(grant): + return Actor(issuer='urn:supervised-maintenance', subject=grant['operator_id'], tenant='tenant:platform', + principal_type=PrincipalType.SERVICE, audience=(), claims={'maintenance_proposal': PROPOSAL}) + +class _Capture: + def __init__(self, ordinary): + self.ordinary = ordinary + self.result = None + def check(self, request): + self.result = self.ordinary.check(request) + return self.result + +class _ExceptionAuthorization: + def __init__(self, actor, correlation, expires): + self.actor, self.correlation, self.expires = actor, correlation, expires + self.used = False + def check(self, request): + now = datetime.now(timezone.utc) + if (self.used or now >= self.expires or request.actor != self.actor + or request.action != 'application.register' or request.tenant != 'tenant:platform' + or request.resource_type != 'user-engine:application' + or request.resource_id != APP.application_id or request.application_id != APP.application_id + or request.correlation_id != self.correlation): + raise Refused('exception outside fixed registration') + self.used = True + return AuthorizationDecision(effect=AuthorizationEffect.ALLOW, reason='explicit supervised maintenance exception; not a PDP allow', + decision_id=None, binding='supervised-maintenance', issued_at=now, + lifetime=min(timedelta(seconds=30), self.expires-now)) + +def execute(connection, ordinary_authorization, grant, *, expected_grant_sha256, + actual_executor_sha256, mode='dry-run', now=None): + now = now or datetime.now(timezone.utc) + expires = validate_grant(grant, expected_grant_sha256=expected_grant_sha256, + actual_executor_sha256=actual_executor_sha256, now=now) + if mode not in ('dry-run', 'apply', 'rollback'): + raise Refused('unknown operation') + store = PostgresUserEngineStore(connection) + if not store.ready: + raise Refused('existing schema required; no migration') + capture = _Capture(ordinary_authorization) + service = UserEngineService(store=store, identity_adapter=object(), authorization=capture) + actor = _actor(grant) + ledger = 'aud_catalogue_bootstrap_' + grant['approval_id'] + correlation = 'maintenance:' + grant['approval_id'] + # Construct both services before BEGIN: the owner readiness probe rolls back + # its connection. Constructing a service in the transaction would discard + # the genuine denial audit and release the native serialization lock. + exception_service = UserEngineService(store=store, identity_adapter=object(), + authorization=_ExceptionAuthorization(actor, correlation, expires)) + outcome = None + try: + with store.transaction(): + with connection.cursor() as cursor: + cursor.execute('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE') + cursor.execute('SELECT pg_advisory_xact_lock(hashtextextended(%s, 0))', ('user-engine:one-app-catalogue:informed-decision',)) + cursor.execute('SELECT clock_timestamp()') + if cursor.fetchone()[0] >= expires: + raise Refused('native transaction grant expired') + previous = _summary(cursor, ledger) + rollback_done = _summary(cursor, ledger + '_rollback') + if mode == 'rollback': + if previous is None or rollback_done is not None or not previous['created']: + raise Refused('no unused created-only recovery authority') + if previous['grant_sha256'] != expected_grant_sha256: + raise Refused('recovery grant differs from consumed authority') + if store.application(APP.application_id) != APP or store.binding(APP.application_id) != BINDING: + raise Refused('created metadata changed') + cursor.execute("SELECT COUNT(*) FROM user_engine_records WHERE application_id = %s AND record_type NOT IN ('applications', 'application_bindings')", (APP.application_id,)) + if cursor.fetchone()[0]: + raise Refused('later dependent records exist') + # Match the existing delivery worker's session advisory lock namespace. + for event_id in previous['event_ids']: + cursor.execute('SELECT pg_advisory_xact_lock(hashtextextended(%s, 0))', ('user-engine:tenant-lifecycle:outbox-event:' + event_id,)) + for event_id in previous['event_ids']: + cursor.execute('SELECT event_id FROM user_engine_outbox_events WHERE event_id = %s FOR UPDATE', (event_id,)) + if cursor.fetchone() is None: + raise Refused('created event missing') + event = store.outbox_event(event_id) + if (event is None or event.correlation_id != correlation or event.claimed_by is not None + or event.claimed_at is not None or event.delivered_at is not None): + raise Refused('claimed/delivered event needs owner reconciliation') + store.save_outbox(replace(event, dead_lettered_at=now, + failure_reason='supervised bootstrap rolled back before delivery')) + for row in records(): + cursor.execute('DELETE FROM user_engine_records WHERE record_type = %s AND record_key = %s AND payload = %s::jsonb', + (row['record_type'], row['record_key'], json.dumps(row['payload']))) + if cursor.rowcount != 1: + raise Refused('created-only compare-and-delete failed') + store.append_audit(AuditRecord(ledger + '_rollback', actor, 'application.metadata.bootstrap.rollback', APP.application_id, + actor.tenant, correlation, summary=json.dumps({'maintenance_authority': grant['approval_id'], 'grant_sha256': expected_grant_sha256, + 'removed': ['applications', 'application_bindings'], 'events_retained_dead_lettered': previous['event_ids']}))) + outcome = {'result': 'rolled-back', 'ordinary_pdp_allow_claimed': False, 'created_entities_removed': 2, 'audit_and_event_history_retained': True} + else: + if previous is not None: + raise Refused('native single-use grant already consumed') + a, b = store.application(APP.application_id), store.binding(APP.application_id) + if (a is None) != (b is None) or (a is not None and (a != APP or b != BINDING)): + raise Refused('partial or conflicting metadata') + created = a is None + if created: + try: + service.register_application(actor, APP, binding=BINDING, correlation_id=correlation) + except AuthorizationDenied: + refusal = capture.result + if (refusal is None or refusal.allowed or refusal.decision_id is None + or refusal.reason == 'authorization service unavailable'): + raise Refused('real ordinary PDP refusal required') + else: + raise Refused('ordinary authorization unexpectedly allowed; use normal owner path') + exception_service.register_application(actor, APP, binding=BINDING, correlation_id=correlation) + if store.application(APP.application_id) != APP or store.binding(APP.application_id) != BINDING: + raise Refused('post-write readback differs') + event_ids = _event_ids(cursor, correlation) + for event_id in event_ids: + event = store.outbox_event(event_id) + store.save_outbox(replace(event, payload={**event.payload, 'maintenance_exception': True, + 'proposal_id': PROPOSAL, 'approval_id': grant['approval_id'], 'operator_id': actor.subject})) + summary = {'created': created, 'grant_sha256': expected_grant_sha256, 'records_sha256': grant['records_sha256'], + 'executor_sha256': actual_executor_sha256, 'approval_receipt_sha256': grant['approval_receipt_sha256'], + 'ordinary_pdp_denial_id': capture.result.decision_id if created else None, 'event_ids': event_ids, + 'authority': grant['authority'], 'not_human_authentication': True} + store.append_audit(AuditRecord(ledger, actor, 'application.metadata.bootstrap', APP.application_id, + actor.tenant, correlation, summary=json.dumps(summary))) + outcome = {'result': 'created' if created else 'no-change', 'ordinary_pdp_refusal_retained': created, + 'ordinary_pdp_allow_claimed': False, 'created_entities': 2 if created else 0, + 'created_event_ids': event_ids, 'grant_native_single_use': True} + cursor.execute('SELECT clock_timestamp()') + if cursor.fetchone()[0] >= expires: + raise Refused('grant expired before transaction commit') + if mode == 'dry-run': + raise _DryRun(outcome) + except _DryRun as dry: + return {**dry.outcome, 'transaction_committed': False, 'dry_run_rolled_back': True} + return {**outcome, 'transaction_committed': True} + +class _DryRun(Exception): + def __init__(self, outcome): + self.outcome = outcome + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--grant-file', required=True, type=Path) + parser.add_argument('--expected-grant-sha256', required=True) + parser.add_argument('--mode', choices=['dry-run','apply','rollback'], default='dry-run') + args = parser.parse_args() + # Whole stdout/stderr, including dependency/driver errors, stays contained. + saved = os.dup(1) + null = os.open(os.devnull, os.O_WRONLY) + os.dup2(null, 1); os.dup2(null, 2) + try: + info = args.grant_file.lstat() + if (not stat.S_ISREG(info.st_mode) or info.st_uid != os.getuid() + or stat.S_IMODE(info.st_mode) != 0o600 or info.st_size > 8192): + raise Refused('private reviewed grant artifact required') + grant = json.loads(args.grant_file.read_text()) + source_hash = hashlib.sha256(Path(__file__).read_bytes()).hexdigest() + import psycopg + from user_engine.adapters.flex_auth import FlexAuthHTTPAdapter + ordinary = FlexAuthHTTPAdapter(base_url=os.environ['USER_ENGINE_FLEX_AUTH_URL'], + bearer_token_file=os.environ['USER_ENGINE_FLEX_AUTH_TOKEN_FILE'], timeout_seconds=3) + with psycopg.connect(os.environ['USER_ENGINE_DATABASE_URL']) as connection: + result = execute(connection, ordinary, grant, expected_grant_sha256=args.expected_grant_sha256, + actual_executor_sha256=source_hash, mode=args.mode) + status = 0 + except Exception: + result, status = {'result': 'refused-or-rolled-back', 'raw_output_suppressed': True}, 2 + os.dup2(saved, 1); os.close(saved); os.close(null) + print(json.dumps(result, sort_keys=True)) + return status + +if __name__ == '__main__': + raise SystemExit(main()) diff --git a/tools/run_approved_catalogue_bootstrap.py b/tools/run_approved_catalogue_bootstrap.py new file mode 100644 index 0000000..75bed6e --- /dev/null +++ b/tools/run_approved_catalogue_bootstrap.py @@ -0,0 +1,62 @@ +"""Prepared supervisor-only transport. NEVER run before founder exception approval. +No credentials leave the workload. Source/grant are delivered only on stdin. +""" +import argparse,base64,hashlib,json,os,stat,subprocess,sys +from pathlib import Path + +def main(): + p=argparse.ArgumentParser(description=__doc__) + p.add_argument('--executor',type=Path,required=True) + p.add_argument('--executor-sha256',required=True) + p.add_argument('--grant-file',type=Path,required=True) + p.add_argument('--grant-sha256',required=True) + p.add_argument('--expected-pod-uid',required=True) + p.add_argument('--mode',choices=['dry-run','apply','rollback'],default='dry-run') + a=p.parse_args() + try: + st=a.grant_file.lstat() + if not stat.S_ISREG(st.st_mode) or st.st_uid!=os.getuid() or stat.S_IMODE(st.st_mode)!=0o600 or st.st_size>8192:raise ValueError('private grant required') + source=a.executor.read_bytes();grant=json.loads(a.grant_file.read_text()) + sha=lambda b:hashlib.sha256(b).hexdigest() + canonical=lambda x:json.dumps(x,sort_keys=True,separators=(',',':')).encode() + if sha(source)!=a.executor_sha256 or sha(canonical(grant))!=a.grant_sha256:raise ValueError('source/grant mismatch') + packet={'source':base64.b64encode(source).decode(),'executor_sha256':a.executor_sha256,'grant':grant,'grant_sha256':a.grant_sha256,'mode':a.mode} + native=""" +import base64,hashlib,json,os,sys +payload=json.loads(base64.b64decode('__PACKET__')) +saved=os.dup(1);null=os.open(os.devnull,os.O_WRONLY);os.dup2(null,1);os.dup2(null,2) +try: + source=base64.b64decode(payload['source']) + if hashlib.sha256(source).hexdigest()!=payload['executor_sha256']:raise ValueError('source differs') + ns={'__name__':'supervised_catalogue_maintenance'};exec(compile(source,'','exec'),ns) + import psycopg + from user_engine.adapters.flex_auth import FlexAuthHTTPAdapter + ordinary=FlexAuthHTTPAdapter(base_url=os.environ['USER_ENGINE_FLEX_AUTH_URL'],bearer_token_file=os.environ['USER_ENGINE_FLEX_AUTH_TOKEN_FILE'],timeout_seconds=3) + with psycopg.connect(os.environ['USER_ENGINE_DATABASE_URL']) as conn: + result=ns['execute'](conn,ordinary,payload['grant'],expected_grant_sha256=payload['grant_sha256'],actual_executor_sha256=payload['executor_sha256'],mode=payload['mode']) +except Exception:result={'result':'refused-or-rolled-back','raw_output_suppressed':True} +os.dup2(saved,1);os.close(saved);os.close(null);print(json.dumps(result,sort_keys=True)) +""".replace('__PACKET__',base64.b64encode(canonical(packet)).decode()) + remote=""" +import base64,json,subprocess,sys +r=subprocess.run(['kubectl','get','pods','-n','user-engine','-l','app.kubernetes.io/name=user-engine','-o','json'],capture_output=True,text=True) +try: + pods=[p for p in json.loads(r.stdout)['items'] if p['metadata']['uid']=='__UID__' and p.get('status',{}).get('phase')=='Running' and any(c['type']=='Ready' and c['status']=='True' for c in p.get('status',{}).get('conditions',[]))] + if r.returncode or len(pods)!=1:raise ValueError('fixed pod not ready') + pod=pods[0];portal=[c for c in pod['spec']['containers'] if c['name']=='portal'] + if len(portal)!=1 or portal[0]['image']!='forgejo.coulomb.social/coulomb/user-engine@sha256:f239bbf332b8eb87320c86390fddca1dcb4f7c628a7af75e79dee8058d446a7b':raise ValueError('source runtime differs') + r=subprocess.run(['kubectl','exec','-i','-n','user-engine',pod['metadata']['name'],'-c','portal','--','python3','-'],input=base64.b64decode('__NATIVE__'),capture_output=True) + out=json.loads(r.stdout) + if r.returncode:raise ValueError('exec failure') +except Exception:out={'result':'refused-or-rolled-back','raw_output_suppressed':True} +print(json.dumps(out,sort_keys=True)) +""".replace('__UID__',a.expected_pod_uid).replace('__NATIVE__',base64.b64encode(native.encode()).decode()) + import uuid + uuid.UUID(a.expected_pod_uid) # only UUID reaches the fixed remote literal + r=subprocess.run(['ssh','railiance01','python3 -'],input=remote,text=True,capture_output=True) + out=json.loads(r.stdout) + if r.returncode:raise ValueError('transport failure') + except Exception:out={'result':'refused-or-rolled-back','raw_output_suppressed':True} + print(json.dumps(out,sort_keys=True)) + return 0 if out.get('result') in ('created','no-change','rolled-back') else 2 +if __name__=='__main__':raise SystemExit(main()) diff --git a/workplans/ADHOC-2026-10-11.md b/workplans/ADHOC-2026-10-11.md index c388def..ddb4c32 100644 --- a/workplans/ADHOC-2026-10-11.md +++ b/workplans/ADHOC-2026-10-11.md @@ -48,3 +48,22 @@ with no skips; external journey acceptance remains incomplete. Evidence: source repair and bounded verification; exact new native CI execution, release risk disposition and real workload acceptance remain USER-WP-0026-T03 / USER-WP-0028-T02. No runtime build input, live policy or image is changed. + +## Independently review exact one-app supervised catalogue maintenance source + +```task +id: USER-WP-ADHOC-2026-10-11-T03 +status: done +priority: high +``` + +Dedicated User Engine owner source review under INTENT Engine/PIP and protected +mutation boundaries. Exact nine-file candidate manifest SHA256 +0ff545d26a0fd72f06040fe377de3efdcc3bd62cdcb6f653247c8ba72f3f34a7 +reviewed independently. All 21 current catalogue checks pass on a disposable +network-none PostgreSQL fixture and installed f239 runtime dependency image, +both limited to 512Mi/1CPU. Formal source-only agreement and retained failed +preparation history: docs/reviews/2026-10-11-catalogue-maintenance-owner-agreement.md +and docs/evidence/2026-10-11-one-app-catalogue-executor/owner-final-postgres-receipt.json. +No founder approval, active waiver, native invocation, credential access or live +write is created. USER-WP-0026-T03 and USER-WP-0028-T02 remain unmet.