control-plane-api, tables: legacy decisions migrate to effective capability bits - #3422
Open
bbartman wants to merge 10 commits into
Open
control-plane-api, tables: legacy decisions migrate to effective capability bits#3422bbartman wants to merge 10 commits into
bbartman wants to merge 10 commits into
Conversation
Contributor
Author
|
I need someone with more knowledge of the change from |
This was referenced Aug 27, 2026
bbartman
force-pushed
the
bmb/3376-stack-6-legacy-decisions
branch
from
August 28, 2026 12:17
7f65fd6 to
3e1c7d4
Compare
bbartman
force-pushed
the
bmb/3376-stack-6-legacy-decisions
branch
from
August 28, 2026 16:59
14f6c9f to
9c4b9c1
Compare
bbartman
force-pushed
the
bmb/3376-stack-6-legacy-decisions
branch
from
August 31, 2026 12:45
9c4b9c1 to
2f44eed
Compare
…abel A single walk reduction returning both halves of a node's authority for one name: mask-attenuated effective bits accumulated additively across covering nodes (the decision input), and the max un-attenuated legacy label (compatibility metadata, None under bundles-only coverage). Request-path consumers that today gate on get_user_capability's legacy label migrate onto the bits half.
attach_user_capabilities hands its closures both halves of get_user_authorization: mask-attenuated effective bits (the decision input) and the un-attenuated legacy label (reporting metadata). LiveSpecRef construction derives userCapability from the bits — null when they fall short of Viewer, otherwise the literal legacy label with none standing in for bundles-only coverage — so the field gates and the writtenBy/readBy minimum-capability filter (now a CapabilitySet superset check, spelled in the bundle vocabulary) no longer consult the legacy label for any decision. A bundles-only viewer grant now lists and reads specs exactly like a legacy read grant.
The listing's rows are authorized by the effective-bits prefix pre-filter, so a row whose covering grants carry no legacy label — a bundles-only grant — is a legitimate outcome, not an error: its userCapability reports the literal column value, none. Closes #3232.
The listing pre-filters rows on effective bits, so a covering grant with no legacy label is a legitimate outcome: userCapability reports the literal column value, none, where the absent label previously tripped the resolver's expect.
writtenBy/readBy admit bundles-only-covered names on effective bits (labeled none) while withholding unauthorized names entirely, and a mask below the Viewer threshold is refused at the liveSpecs root with the structured missing-capabilities body — reached grants' legacy labels are never a side-channel around the mask.
get_user_capability's callers all consume get_user_authorization now, whose legacy half carries the identical semantics: the label of a reached node reflects storage, un-attenuated, and never authorizes. Its tests pin that half of the combined reduction.
alertConfigs and inviteLinks spell their filtered_authorized_prefixes minimums as CapabilityBundle::Viewer and CapabilityBundle::Admin — bit-identical to the legacy enums they replace, and the vocabulary the rest of the mask stack speaks. The storage-mapping mutation responses' Admin literals gain the comment stating they are informational metadata backed by the mutations' admin-level requirement.
UserAuthorization pairs the effective bits with the legacy label as a named type, with legacy_label() as the single owner of the reporting rule that none stands in for bundles-only coverage — the shape the listing resolvers each restated in comments. storageMappings' edge collection drops its Result plumbing: the closure is infallible.
writesTo refs are un-filtered, so one query exercises both outcomes of the effective-bits gate: an accessible bundles-only referent serves its gated fields under the none label, and a referent outside the caller's grants presents with userCapability null and every gated field null — the coupling LiveSpecRef's field docs promise.
The userCapability docstring gained legacy-metadata semantics in this branch, but the committed SDL was not regenerated to match.
bbartman
force-pushed
the
bmb/3376-stack-6-legacy-decisions
branch
from
September 1, 2026 13:08
2f44eed to
e87c6d1
Compare
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.
Task 4 of #3376 (stacked on #3406): every remaining request-path authorization and visibility decision that read the legacy
read/write/adminlabel now keys on mask-attenuated effective capability bits. The legacy label is reporting metadata only, per decision 3 of the implementation plan.Closes #3232.
What changes
tables::UserGrant::get_user_authorizationis a new single-name walk reduction returning both halves of a name's authority: effective bits accumulated additively across covering nodes (the decision input), and the max un-attenuated legacy label (compatibility metadata,Noneunder bundles-only coverage). It supersedesget_user_capability, which is removed; the label semantics are pinned by the converted tests.LiveSpecRef: the six gated sub-resolvers (liveSpec,activeAlerts,alertHistory,status,lastPublication,publicationHistory) and thewrittenBy/readByminimum-capability pagination filter (previously anOrdcomparison on the legacy enum) decide on effective bits.userCapabilityis null iff the bits fall short of Viewer; otherwise it reports the literal legacy column, withnonestanding in for bundles-only coverage.storageMappingsno longer errors the whole query when a row's covering grants carry no legacy label (the storageMappings query fails for bundle-only grants (legacy-capability mismatch) #3232 bug); the row serves withuserCapability: none.dataPlanesno longer panics on the same shape (.expect("capability guaranteed by pre-filter")trusted the legacy label where the pre-filter guarantees bits).alertConfigsandinviteLinksspell theirfiltered_authorized_prefixesthresholds asCapabilityBundle::Viewer/CapabilityBundle::Admin— bit-identical to the legacy enums they replace.Adminliterals increateStorageMapping/updateStorageMappingresponses stay: the field is informational, the mutations require admin-level effective bits, and no consumer reads it (the onlyuserCapabilityconsumer anywhere is the dashboard's auth-roles store onPrefixRef, untouched here).Behavior changes
Thresholds are unchanged everywhere — only the decision input moves. The one deliberate behavior change beyond mask-correctness: bundles-only grants (
capability = 'none', non-emptybundles) now work throughliveSpecs,storageMappings, anddataPlaneslistings, consistent with the walk-based checks that already admit them.PrefixRefalready reports these asnone;LiveSpecRef/StorageMapping/DataPlanenow do the same.Explicitly excluded per the plan:
live_specs/db.rs's SQL-computeduser_capabilityandget_connected_live_specs(async-executor-only; task 9 territory), and per-endpoint re-vocabulary (e.g.CreateInviteLinkfor invite links).Tests
tables: the combined reduction (bundles-only, masked attenuation, un-attenuated label, additive multi-path composition), plus the converted legacy-label pins.liveSpecs: a bundles-only viewer lists and reads specs (labelednone);writtenBy/readByadmit bundles-only-covered names and withhold unauthorized names; a sub-Viewer mask is refused at the root with the structured missing-capabilities body, so legacy labels are never a side-channel around the mask.storageMappings/dataPlanes: bundles-only listings serve withnonewhere they previously errored/panicked.Note
The 2026-08-26 Daily Sync recorded an open follow-up ("Discuss Legacy Bits", Brian with Dave and Greg) on exactly this gate-on-bits question. The written record — #3232, #2936's design comment defining
capability: nonefor bundles-only grants, and thePrefixRefdoc — all point the way this PR implements it; that follow-up is the venue to veto before merge.