fix(token): the ERA policy stand-in is deleted and the hole left; the enforcer is keyed by the policy commitment and registers a policy only from its committed bytes - #1016
Merged
Conversation
… enforcer is keyed by the policy commitment and registers a policy only from its committed bytes Placeholder sweep, the last item (owner decision 2026-09-25: delete the fake, leave the hole). CONFORMANCE_GAPS §6.32. - TokenPolicySystem::register_policy(bytes) recomputes BLAKE3(TAG_DSM_POLICY, bytes), reads the TokenPolicyV3 blob with Core's one parser and derives the enforcer's view from it (enforced_policy, moved in from the SDK); policy_at(commit) takes the durable bytes the SDK resolver answers only when they re-hash to the commitment asked for; enforce_policy(commit, ..) denies where no policy is committed. The ERA preload, create_root_token_policy, ticker-keyed registration, the caller-asserted anchor binding, PolicyCommitResolver, the validator module and the cache's ticker index are deleted. - The SDK keys enforcement by the policy_commit the operation carries (Transfer, Burn, CreateToken); a lock operation, which carries none, is refused for it (§9.1). Creation and rehydration hand Core the bytes (CoreSDK::register_policy_bytes); the resolver answers load_policy_verified(commit). - IdentityConstraint, EmissionsSchedule, CreditBundlePolicy, Custom, roles and StoredPolicy deleted; PolicyConditionProto fields 1, 5, 6, 7 and CanonicalPolicy.roles reserved; TypeScript bindings regenerated. BitcoinTapConstraint untouched, recorded. - The hole: ERA_POLICY_COMMIT has no preimage, so every ERA transfer and burn is refused at enforcement until ERA's policy blob exists. The tests this turns red are the expected-red manifest, recorded from this commit's board.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The ERA policy stand-in is deleted and the hole is left visible; the token policy enforcer is keyed by the policy commitment and registers a policy only from the committed bytes it hashed itself. Placeholder sweep, the last item (owner decision 2026-09-25: delete the fake, leave the hole, last commit after the clean checkpoint — #1015's board).
CONFORMANCE_GAPS.md§6.32 records the findings, the state and the hole.This PR is red by design on every suite that transfers or burns ERA, and the expected-red manifest below names each test. From here a board is judged against that list: a red outside it is a regression, and a listed test that goes green without ERA's policy is a finding.
The finding
CoreSDK::new_with_devicepreloaded an "ERA Token Policy"PolicyFileof three metadata strings, registered under the ticker"ERA"at an anchor that is the hash of that projection — notERA_POLICY_COMMIT, which has no preimage: noTokenPolicyV3bytes hash to it, because ERA's release rule is its emission schedule (SoFi §51) and no such blob exists. Around it: the enforcer was keyed by the operation'stoken_idstring;register_token_policy_with_anchorbound a caller-builtPolicyFileto a caller-supplied anchor on the caller's word; a validator checked semver strings over that projection; and four condition kinds the policy grammar (§47–§54) does not name — an identity allowlist over caller-stated strings, an emission schedule, a credit bundle, a custom constraint — plus role-based access control lived in the type, the enforcer and the proto, three of them "configuration-only" arms that allowed unconditionally.How
dsm/src/core/token/policy/mod.rs:TokenPolicySystem::register_policy(bytes)recomputesBLAKE3(TAG_DSM_POLICY, bytes), reads the blob with Core's one parser and derives the enforcer's view from what it says (policy_enforcement::enforced_policy, moved in from the SDK'sderive_policy_file);policy_at(commit)takes the durable bytes the SDK resolver answers only when they re-hash to the commitment asked for;enforce_policy(commit, ..)denies where no policy is committed. The preload,create_root_token_policy, ticker-keyed registration, the caller-asserted anchor binding,PolicyCommitResolver, the validator module and the cache's ticker index are gone.dsm_sdk: enforcement is keyed by thepolicy_committhe operation carries (Transfer, Burn, CreateToken); a lock operation, which carries none, is refused for it (§9.1). Creation and rehydration hand Core the bytes (CoreSDK::register_policy_bytes) and take the commitment Core answers; the resolver answersload_policy_verified(commit).policy_types.rs/policy_enforcement.rs/proto/dsm_app.proto:IdentityConstraint,EmissionsSchedule,CreditBundlePolicy,Custom, roles andStoredPolicydeleted;PolicyConditionProtofields 1, 5, 6, 7 andCanonicalPolicy.rolesreserved; TypeScript bindings regenerated (npm run proto:gen).BitcoinTapConstraintuntouched (Bitcoin is out of this round), recorded as the configuration-only arm it is.market_legs_permittedis the same absence and is recorded, not changed.Verification (release,
dsm_client/deterministic_state_machine)cargo test --locked --release -p dsm --lib -- core::token::policy types::policy_types core::token::token_state_manager --test-threads=1→ dsm 29/0 (four new tests: a policy registered from its committed bytes alone; bytes at another commitment are not the policy asked for; a cache miss takes the durable bytes that re-hash to the commitment; an operation naming a commitment without a policy is denied; plus the projection test moved from the SDK).cargo test --locked --release -p dsm_sdk --lib -- handlers::token_routes --test-threads=1→ 12/0;--test supply_cap_enforcement --test supply_cap_partial_history→ 4/0, 2/0.make lint→ exit 0. Frontendtsc --noEmiton the regenerated bindings → exit 0.ci/production_safety_checks.sh(incl. the conformance-evidence check; totals regenerated: MR-SOFI-0304 and MR-SOFI-0315 Met),ci/sofi_reachability.py,ci/sofi_validated_root_constructors.sh,ci/sofi_no_default_evidence.sh,ci/admitted_predecessor_readers_fenced.sh→ pass.cargo ndk -t arm64-v8a --platform 23 check --locked --package dsm_sdk --features=jni,bluetooth→ exit 0.Mutation controls (each run, restored, the named tests green again on the restored tree: 4/0)
policy_attaking the durable bytes without re-hashing them to the commitment asked for →bytes_at_another_commitment_are_not_the_policy_asked_forred.enforce_policyallowing where no policy is committed →an_operation_naming_a_commitment_without_a_policy_is_deniedred.dsm::economic::token_policy::tests::a_zero_genesis_supply_is_refusedred.burnpermitted by every policy in the projection moved into Core →the_policy_permits_exactly_what_its_flags_namered.Expected-red manifest
From the
Rust tests (dsm_sdk)job of this commit's board, run 36226702605: 1020 tests, 46 failed). The 45 below fail on the ERA refusal — "Token policy violation forNW9MKEFNZ6GTD8209QN3DQ6996DWP9E9NQ0H5DYCKA9WNS0Z69H0: no policy is committed at the commitment the operation names" — at an ERA transfer or burn. Every other job of the board is green (Rust tests (dsm),workspace-rest,Storage Node (Postgres), the gates, Lean). A red outside this list is a regression; a listed test that goes green without ERA's policy is a finding.dsm_sdk::bluetooth::offline_step_tests::an_online_send_waits_for_the_offline_step_in_flightdsm_sdk::handlers::bilateral_finality_tests::a_send_before_the_previous_step_finalizes_is_gated_never_marked_for_resyncdsm_sdk::handlers::bilateral_finality_tests::harness_carries_one_generation_a_to_b_through_production_codedsm_sdk::handlers::bilateral_finality_tests::r11_only_the_checkpoint_sweep_clears_the_gatedsm_sdk::handlers::bilateral_finality_tests::r1_role_reversal_applies_once_on_a_and_finalizes_on_bdsm_sdk::handlers::bilateral_finality_tests::r2a_recipient_cannot_originate_before_the_peer_finalizeddsm_sdk::handlers::bilateral_finality_tests::r2b_the_certificate_releases_the_recipientdsm_sdk::handlers::bilateral_finality_tests::r3_sender_stays_gated_until_the_checkpoint_reaches_quorumdsm_sdk::handlers::bilateral_finality_tests::r4_calibration_cannot_release_the_sender_gatedsm_sdk::handlers::bilateral_finality_tests::r7_a_frozen_checkpoint_is_replayed_byte_identically_after_the_fleet_returnsdsm_sdk::handlers::bilateral_finality_tests::r8_a_next_generation_transfer_is_held_until_the_certificate_landsdsm_sdk::handlers::bilateral_finality_tests::r9_the_barrier_is_relationship_localdsm_sdk::handlers::bilateral_finality_tests::the_harness_defers_the_background_poller_while_a_pair_livesdsm_sdk::handlers::node_e2e_tests::a_transfer_reaches_the_nodes_only_sealed_and_arrivesdsm_sdk::handlers::node_e2e_tests::an_inbox_read_that_did_not_cover_every_delivery_is_not_a_complete_syncdsm_sdk::handlers::recipient_accept::tests::a_bad_receipt_sig_is_refused_and_never_acceptsdsm_sdk::handlers::recipient_accept::tests::a_bad_sig_a_is_refused_and_never_acceptsdsm_sdk::handlers::recipient_accept::tests::verification_is_unreachable_from_a_single_halfdsm_sdk::handlers::recipient_admission_tests::a_transfer_admits_on_both_sides_with_a_register_backed_releasedsm_sdk::handlers::recipient_admission_tests::an_outage_holds_the_transfer_cleanly_and_it_recoversdsm_sdk::handlers::recipient_admission_tests::fabricated_sender_coordinates_are_refused_before_any_durable_statedsm_sdk::handlers::recipient_admission_tests::the_same_sender_debit_cannot_fund_a_second_creditdsm_sdk::handlers::recipient_dispatch::tests::a_single_half_or_an_unbound_half_never_completesdsm_sdk::handlers::recipient_dispatch::tests::a_tampered_evidence_arriving_first_cannot_lock_out_the_honest_copydsm_sdk::handlers::recipient_dispatch::tests::a_tampered_transfer_arriving_first_cannot_lock_out_the_honest_copydsm_sdk::handlers::recipient_dispatch::tests::every_arrival_order_convergesdsm_sdk::handlers::recipient_dispatch::tests::staging_freezes_the_exact_bytes_and_the_frozen_pair_is_what_appliesdsm_sdk::handlers::relationship_finalized::tests::a_certificate_for_a_transition_never_journaled_is_not_oursdsm_sdk::handlers::relationship_finalized::tests::a_verified_certificate_releases_the_recipient_once_and_forgeries_do_notdsm_sdk::handlers::sender_admission_tests::a_failed_finish_holds_the_outbox_and_resume_completes_the_same_admissiondsm_sdk::handlers::sender_admission_tests::a_stale_admission_snapshot_is_refused_not_committeddsm_sdk::handlers::sender_admission_tests::a_stale_resume_returns_the_admitted_outcome_and_leaves_a_newer_admission_alonedsm_sdk::handlers::sender_admission_tests::a_transfer_registers_the_senders_root_at_the_next_positiondsm_sdk::handlers::sender_admission_tests::an_admitted_burn_advances_the_lineage_and_is_foreign_walkabledsm_sdk::handlers::sender_admission_tests::sequential_admissions_stay_monotonic_across_operation_kindsdsm_sdk::handlers::sender_admission_tests::token_routes_admit_create_and_burn_end_to_enddsm_sdk::handlers::storage_routes::tests::a_delta_for_a_step_with_no_retained_evidence_is_a_terminal_invariant_violationdsm_sdk::handlers::storage_routes::tests::a_full_receipt_on_the_countersign_method_is_refused_at_the_wiredsm_sdk::handlers::storage_routes::tests::a_poisoned_delta_parks_the_step_and_the_honest_delta_still_finalizesdsm_sdk::handlers::storage_routes::tests::a_release_of_another_step_cannot_finalize_this_onedsm_sdk::handlers::storage_routes::tests::settled_outbox_rows_are_never_resubmitteddsm_sdk::sdk::b0x_sdk::tests::a_send_lands_on_exactly_the_register_quorum_of_membersdsm_sdk::sdk::b0x_sdk::tests::delivery_below_the_quorum_is_refused_however_many_members_are_marked_faileddsm_sdk::sdk::core_sdk::tests::a_redelivered_transfer_applies_once_and_is_never_rebuiltdsm_sdk::sdk::core_sdk::tests::an_apply_consults_the_signed_pair_never_the_projectionNot in the manifest:
dsm_sdk::sdk::sofi_reads::tests::an_unestablished_genesis_candidate_is_not_read_as_unpublished, red onmainsince #1013 for a reason of its own (it booted no device) and fixed by #1017.