Skip to content

Auth pattern decision procedure: which authorization pattern, when (WIF / ntfy approval / manual) — selection, census, conformance row - #11734

Merged
briansrls merged 8 commits into
mainfrom
session/nimble-otter-774
Sep 20, 2026
Merged

briansrls merged 8 commits into
mainfrom
session/nimble-otter-774

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

Which authorization pattern, and when — one decision procedure under gunbc.auth

Trigger (operator, 2026-09-19). The R2 origin work (#11713, #11721) minted Cloudflare tokens and created Secret Manager containers under an operator GCP access token pasted into chat three times and relayed between sessions by message. The same day the ntfy approval loop executed on hardware (fleet-converge run 35447067113: request → phone tap → capability redeemed → BMC credential minted) and workload identity federation had been fetching credentials for dispatched runs (boot run 35249658152). Three patterns existed; nothing told an author which to use.

What lands here (PR 1 of a sequence — model, census, conformance row; no call-site migration).

  1. gunbc.auth.authorization_pattern_selection — the decision procedure as a §3d realization selection. It consumes std.decision select_realization (no second decision algebra): the privileged effect is the subject, its characteristics are the bound context parameters, the laws are hard constraints that exclude candidates before Pareto, and the survivors are ranked on three funded axes (human actions per effect, credential scoped to the resource, receipt in the substrate). Output is PatternSelected { pattern, receipt }, ManualStepRequired { step, receipt } (the (c) arm: a typed refusal naming the std.human_intervention row), or std.decision's refusal arms carried through.
  2. gunbc.auth.privileged_effect_census — every privileged-effect site in dag/ (module + symbol, verified against each file's module line), each with its effect characteristics and what it realizes today. The three-valued verdict is derived, not authored: the fold runs the selection over each row and compares; the author may state a divergence reason, never the verdict. The unreasoned divergences are joined at identity grain against a ranked follow_up_sites roster in both directions (a new silent divergence is red; a paid one whose row was not retired is red). A stated reason is a typed DivergenceReason { statement, dissolution } over std.dissolution: bound triggers name the declaration whose retirement fires them (checked by the floor's citation gate), unbound ones name the capability that would; the witness asserts no reasoned divergence's trigger has fired.
  3. Conformance row — conformance-leasing in gunbc.design_argument conformance_domains extended (not a new row: "who may do this and how was it granted" is one question) with the gunbc.auth selection, federation, grant, request, capability, redemption, token-source and census homes, and four pasted-credential tells. This regenerates DESIGN.md — load-bearing. Regen procedure followed: ./target/release/gunbc run --source-root dag --source-root src/v2 --entry dag/gunbc/instruments/docs_projection_gate.dag --function regen, run on a clean tree first (byte-identical) and again after the row edit; only the files this change owns moved.
  4. Witness test.claim.authorization_pattern_selection_witness — fixtures at the interface for each arm, plus the inhabitance claims that run the real census through the real fold.

The procedure

                     privileged effect
                            │
              ┌─────────────┴──────────────┐
              │ Is there a provider API?   │
              └─────────────┬──────────────┘
                 no         │        yes
        ┌───────────────────┘        │
        ▼                            ▼
 ManualStepRequired         ┌────────────────────────────────────────────┐
 { step }  ── names the     │ Did the credential reach this process on   │
 std.human_intervention     │ the path its issuer issued it on?          │
 row (e.g. Cloudflare       │ (pasted / relayed / cross-session token)   │
 account-token bootstrap)   └────────────────────┬───────────────────────┘
                                      no         │         yes
                              ┌──────────────────┘          │
                              ▼                             ▼
                     REFUSED before Pareto        ┌────────────────────────────┐
                     (PastedOperatorToken         │ witness required?          │
                      fails ax_credential_custody)│  irreversible, OR mints a  │
                                                  │  project/account-wide      │
                                                  │  credential, OR bills      │
                                                  │  externally, OR break-glass│
                                                  └──────────────┬─────────────┘
                                             yes                 │              no
                             ┌───────────────────────────────────┘               │
                             ▼                                                   ▼
              ┌──────────────────────────────┐              ┌────────────────────────────────────┐
              │ recurring (EveryProvision)?  │              │ workload identity bindable?        │
              └──────────────┬───────────────┘              │ (a federated principal the provider│
                yes          │          no                  │  admits for THIS effect)           │
        ┌────────────────────┘          │                   └──────────────────┬─────────────────┘
        ▼                               ▼                        yes           │          no
 NoAdmissiblePattern         (b) OperatorApprovedCapability       │            │
 (a recurring effect that    access_request → submission → ntfy  ▼            ▼
  owes a witness is a        tap → capability → broker redeem →  (a) Federated  (b) Operator-
  design contradiction:      effect under the adapter's identity   ScopedGrant   Approved-
  make it reversible or      One human action; credential bound    WIF + one     Capability
  make it one-off)           to one request revision; receipt =    per-secret
                             the decision store row                cell; zero
                                                                   human actions;
                                                                   receipt = run

(d) OperatorOwnSession — the operator's own interactive gcloud or own token file — is in the field and is always dominated (one human action like (b), project-wide credential unlike (b), no receipt unlike (b)); its exclusion is computed by the fold, not by a rule someone could delete, so a site still on it classifies as diverges rather than unclassifiable.

What replaces pasting an operator token. A dispatched run: (a) — it already has an identity (WIF_ACCESS_TOKEN), and needs cells, not a token. A one-off control-plane effect in a session (accessor-cell grants, container creation, irreversible mints): (b) — file the request, tap, and the effect executes under the authority adapter's identity. Until the adapter for GCP IAM / Cloudflare effects lands (first executing consumer: #11484), the interim is the grant administrator's own session on the grant administrator's own workstation, marked diverges-with-reason in the census — and relaying that token through chat or a message into another session's GUNBC_GCP_ACCESS_TOKEN_FILE is the refused pattern. The substrate cannot see how a file was filled, so the conformance tell is the reviewer's.

Census (derived by executing the selection over the roster; the consumer is test.claim.authorization_pattern_selection_witness — every_census_site_is_decidable and unreasoned_divergences_join_the_follow_up_roster_exactly; the table below was read off the same fold through the interpreter)

site (module · symbol) selected verdict
gunbc.auth.patterns credential_chain (a) federated conforms
gunbc.auth.heal_publisher_federation heal_publisher_wif_pool (a) federated conforms
gunbc.runner.runner_jit_perform dispatch_jit_mint (a) federated conforms
gunbc.tools.bmc_health_reader_converge bmc_health_reader_converge (a) federated conforms
gunbc.tools.bmc_onboard bmc_converge_credential_selected (a) federated conforms
gunbc.cloudflare.r2_permission_group_observe observe_account_permission_groups_standing (a) federated conforms
gunbc.cloudflare.r2_token_mint cloudflare_account_token_bootstrap_intervention (c) manual step conforms
gunbc.cloudflare.r2_token_mint_run r2_mint_bootstrap (b) approval diverges-with-reason: no Cloudflare approval adapter; workflow_dispatch was the informal witness; the session-run token was relayed from chat
gunbc.cloudflare.r2_mint_secret_access r2_mint_bootstrap_read_access_converge (+3 cells; #11721 adds three r2_bucket_admin_*_access_converge cells of the same class) (b) approval diverges-with-reason: GCP IAM approval adapter unlanded (#11484)
gunbc.auth.fleet_secret_accessor_roster fleet_accessor_grants_converge_with_supplied_token (b) approval diverges-with-reason: same
gunbc.spark.secret_access_ensure spark_secret_access_converge (b) approval diverges-with-reason: same
gunbc.fleet.org_actions_converge org_admin_app_key_access_converge_with_supplied_token (b) approval diverges-with-reason: same
gunbc.auth.heal_publisher_provision heal_publisher_federation_provision_with_supplied_token (b) approval diverges-with-reason: same
gunbc.fleet.printer_credential_migration_run run (b) approval diverges-with-reason: same
gunbc.auth.approval_mac_key_provision provision_approval_mac_keys (b) approval diverges-with-reason: bootstrap circularity — the loop cannot provision its own keys (structural, not interim)
gunbc.assimilate.bmc_bootstrap_provision bmc_bootstrap_provision_srv3 (b) approval diverges-with-reason: declared §3 fork (ensure_access_token_eager_bootstrap_fork_dissolve_on)
gunbc.gcp_estate_read_instrument gcloud_read_argv (b) approval (no SA holds org-wide read) diverges — follow-up 1
gunbc.auth.credentials gcp_secret_credential (a) federated diverges — follow-up 2 (eager gcloud; legacy)
gunbc.bmc_fan_converge bmc_fan_run (a) federated diverges — follow-up 3
gunbc.srv3_bmc_credential_resolve materialize_bmc_login_password (a) federated diverges — follow-up 4
gunbc.tools.bmc_onboard srv3_converge_credential (+srv4, probe) (a) federated diverges — follow-up 5
gunbc.fleet.printer_delivery_wet_run run (a) federated diverges — follow-up 6
tools.fabric_m0_origin_object_probe probe_token_source (a) federated diverges — follow-up 7
gunbc.spark.managed_access_apply spark_managed_access_apply_wet (a) federated diverges — follow-up 8 (its 2026-08-13 frontier reason's trigger has fired; a fired reason is no reason — review 68577)
gunbc.fleet_multi_principal_probe fleet_multi_principal_probe_with_attempt (a) federated diverges — follow-up 9 (same)

Kernels parameterized by a token source (gunbc.auth.gcp_secret_access secret_access_ensure_for, gunbc.auth.secret_ref_credential, gunbc.auth.secret_rotation, gunbc.secret_provision_actuator, gunbc.fleet_printer_access) choose nothing and are not rows. Completeness of the roster is not claimed: the population was enumerated by grep on 2026-09-19 and a discovery join that refuses an unrostered site is the named next rung (same trigger gunbc.host_convergence_census names).

Two things the fold corrected in my first cut (kept as receipts)

  • Non-idempotency was a witness ground; it made the per-provision JIT runner mint NoAdmissiblePattern. That was a §4d over-prohibition — retry safety is std.effects' concern, consent is reversibility's — so idempotency is not a characteristic here.
  • The two obligation gates (witnessed, unattended) were funded as raw readings and split dominance among survivors on a fact the effect never asked about (two reads came back SelectionNeedsPolicy). They now read as obligation met relative to the effect.

Follow-ups (operator-widened scope: migrate in rank order, sequenced PRs)

  1. R2 mints (r2_token_mint_run run / run_object_write / Converge R2 entitlement + bucket existence for every allocated origin purpose #11721's bucket-admin profile) — dispatched fleet run stays (a); one-off mints route through (b). Requires the approval loop's executing authority adapter for Cloudflare/GCP effects (first consumer approval loop D: converge materialization, loopback bind, mtcollins1_boot mode #11484). Coordinated with sharp-ant-20 (messaged; I do not edit its branch).
  2. The nine follow_up_sites above, in roster order — mostly one seam each: GcloudPrintToken/read_supplied_access_token spelled in the entry → resolve_access_token.
  3. The GCP IAM accessor-cell converges (six sites) move from the operator's session to (b) when the adapter lands.

Not done here / honest limits

  • No call site is migrated in this PR (brief item 5 as originally stated; the widened scope follows in sequenced PRs).
  • heal_publisher_federation is cited by its pool declaration because the module is declarations, not an entry.
  • The witness modules for gunbc.auth are not in required_gate_prefixes; this one follows its siblings.

Do not self-merge — operator to land.

🤖 Generated with Claude Code

Brian Searls and others added 2 commits September 19, 2026 18:21
… selection, census, conformance row

gunbc.auth.authorization_pattern_selection models "which authorization pattern
performs this privileged effect" as a §3d realization selection over
std.decision: the effect's characteristics (frequency, reversibility, provider
surface, workload-identity binding, minted reach, billing) are the bound
context parameters; custody, surface fit, identity binding and the two derived
obligations (witness, unattended) are hard constraints that exclude candidates
before Pareto; survivors are ranked on human actions per effect, credential
scope and receipt. Three patterns are ever selected -- workload identity
federation with a scoped grant, per-effect operator approval through the ntfy
loop, a named human-only step -- and an operator token relayed by chat or
between sessions is excluded every time by the custody constraint.

gunbc.auth.privileged_effect_census classifies every privileged-effect site in
dag/ by EXECUTING that selection: the three-valued §3b verdict is derived, the
author states only a divergence reason. Unreasoned divergences join a ranked
follow-up roster at identity grain in both directions.

The conformance-leasing row is extended (not a new row) with the gunbc.auth
homes and pasted-credential tells; DESIGN.md regenerated through
gunbc.instruments.generated_artifact_gate main_wet_one (one bullet moved).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ccess_apply and fleet_multi_principal_probe join the follow-up roster (review 68577)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor Author

review 68577 (workstation_wet_run_frontier): agreed and fixed in 5a15847. A reason whose own text says its trigger has fired is not a reason, so both rows now carry divergence_reason: none, sit in follow_up_sites (ranks 8 and 9), and the stale reason row is deleted. Re-ran census_gate (exit 0) and unreasoned_divergences_join_the_follow_up_roster_exactly (true) through the interpreter; PR body table updated (7 conform / 8 with-reason / 9 diverge).

— sent from nimble-otter-774

Brian Searls and others added 2 commits September 19, 2026 20:14
…refuses a // inside a declaration body

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… census's consumer; a second route to the same predicates was inert (review 68634)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor Author

review 68634 (census_gate unconsumed): agreed, fixed in 664ecbb. Deleted census_gate, render_census, verdict_line, conformance_text and selection_text — the witness (every_census_site_is_decidable, unreasoned_divergences_join_the_follow_up_roster_exactly) is the census's consumer and already executes the same predicates, so the gate was a second route to one fact. Both witness probes re-run green through the interpreter; PR body no longer cites the gate.

— sent from nimble-otter-774

…ide (review 68656); constraint evidence says what a gate does rather than what it excluded

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor Author

review 68656 (minted_reach on the Cloudflare bootstrap row): agreed, fixed in f17c9ea — the account-owned token that mints further account tokens is MintsAccountWideCredential by the module's own definition; MintsProjectWideCredential is no longer instantiated by the census. Also took the constraint-evidence prose you chose not to raise: it now says what the gate does ("a gate at 1 applied to every candidate before dominance") rather than claiming an exclusion that may not have happened. Verdict-neutral, witness re-run green.

— sent from nimble-otter-774

….spark.bootstrap_provision (review 68671)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor Author

review 68671 (citation): agreed, fixed in 2f34b3d — no_principal_can_both_grant_and_read is now cited by its owning module, gunbc.spark.bootstrap_provision, in both places. Prose/string only; no behaviour change.

— sent from nimble-otter-774

Brian Searls and others added 2 commits September 19, 2026 23:55
…floor: CITED-DECLARATION-ABSENT)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…dissolution), never bare prose; the fired-trigger check is a fold and a witness (review 68720)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor Author

review 68720 (divergence_reason as bare prose): agreed, fixed in a0a3026. DivergenceReason { statement, dissolution: std.dissolution.DissolutionCondition } replaces NonEmptyStr?. The two structural reasons are BOUND (retires_dissolution on approval_submission_mac_key_secret_ref and on ensure_access_token_eager_bootstrap_fork_dissolve_on — deleting either subject is refused by the floor's citation gate, so a fired trigger cannot stand silently); the two adapter-unlanded reasons are UNBOUND with the capability named per §4b(3) (a forward decl_ref to an unwritten adapter is the fabricated-citation class the floor refuses), including the Cloudflare one: an approval-loop adapter performing cloudflare.AccountTokens.Create under a redeemed capability, owner this lane, PR 2b after #11721. New consumer reasoned_divergences_with_fired_triggers() plus witness no_reasoned_divergence_has_a_fired_trigger (green); the roster join and R2 probes re-run green.

— sent from nimble-otter-774

@gunbai-bot

gunbai-bot Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor Author

review 68744: the non-blocking remark (name the input-derivation of a row's characteristics in the census's next-rung trigger) is right and is a prose edit to the roster preamble; I'm folding it into PR 2a, which edits the census anyway when it retires the first follow-ups, rather than spending another CI/review cycle here. Tally met on this head; asking the operator to land.

— sent from nimble-otter-774

@briansrls
briansrls added this pull request to the merge queue Sep 20, 2026
gunbai-bot Bot pushed a commit that referenced this pull request Sep 20, 2026
…this head

Review 69227: the rows' boundary and red-control text cited
gunbc.auth.privileged_effect_census, gunbc.auth.authorization_pattern_selection and
gunbc.cloudflare.r2_bucket_ensure, which resolve only once #11734 and #11721 merge,
and the plan page asserted them as landed. Each citation now names the PR it lands
with, and the page separates what executed from what is approved and unmerged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Merged via the queue into main with commit 7872af5 Sep 20, 2026
2 checks passed
@briansrls
briansrls deleted the session/nimble-otter-774 branch September 20, 2026 16:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant