Repository navigation
Conversation
A ring-based RS256 verifier for Microsoft Entra v2.0 id tokens in obop-auth (no network, no storage): compact JWS shape, algorithm fixed to RS256, keys only from the pinned key set by kid, then issuer, tenant, audience, azp, nonce, times with a 2 minute skew, oid and amr. The discovery document is accepted only for the pinned tenant and with endpoints under it. obop-commands gets the SsoApi port (no network, no session) and Authenticator::sso(). obop-auth implements it: provider configuration with a sealed client secret, administrator-written bindings keyed by hash(issuer|tenant|oid), the sso_only flag with the break-glass guard, the one-time code and the exchange to the same outcome as the password step. SSO-only accounts refuse the password and the passkey on their own. A test stand-in for the provider (feature test-support) and a table of 15 verifier tests that each name a way a token can be wrong. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… rotation (D91) obop-auth: 19 tests of configuration (tenant a GUID, secret sealed and never shown), bindings (one identity one account, keyed by issuer, tenant and object id), the one-time code (hashed, single use, 60 s, rechecked against the account and the credential epoch), the second factor rule (Microsoft's mfa stands for it only on SSO-only accounts), the password and passkey refusals of SSO-only accounts, the break-glass rule and the key rotation of the client secret. An SSO-only account also refuses an administrator-set temporary password. The client secret is listed in the obop-keys rotation inventory and in the obop-ops inventory test. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…CLI (D91) obop-http: GET /api/auth/sso, /oidc/start, /oidc/callback and POST /oidc/exchange. The flow's state, nonce and the hash of the PKCE verifier live in the shared store for ten minutes and are spent once; the verifier is the value of an HttpOnly cookie of the browser that began. The server calls the provider's discovery document, key set and token endpoint through the hardened egress client, verifies the id token through the SsoApi port and drops it. A failure is one redirect with a fixed word; the reason goes to the security log. The one-time code is bound to a second HttpOnly cookie, so it is worth nothing to another browser (login CSRF). obop-pkg: the sso_provider model (configure and clear are refused for agents and not offered on MCP) and bind_sso, unbind_sso, set_sso_only on user_account (an agent can propose, a person decides). obop: obop admin sso status|configure|clear|bind|unbind|only, with the client secret read from stdin. Nine HTTP tests against a stand-in provider on loopback. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…tions and e2e (D91) The exchange of the one-time code returns the account name with the login outcome, the back office reads #oidc= once at load and cleans the address, the button and its messages exist in 7 languages (the six translations are machine drafted), the account commands list on all surfaces and refuse agents when run (D41), and Playwright drives the round trip against a stand-in for Entra on loopback. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…mits D91 in the register with the D53 amendment and a D64 cross-reference, the section in DEPLOY.md, section N of ISSUES.md and a line in the code map. Nothing in these texts is ratified, and nothing ran against a real Entra tenant. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
docs/DECISIONS.md conflicted in the summary table: main's D90 row (merged, OR-37) and this branch's D91 row and note are both kept, in that order. CLAUDE.md and docs/ISSUES.md merged without conflict. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…every path, hardening) Answers the review of the build (155 findings). Scope is now `openid profile` (Entra sends the object id only with `profile`), `amr` and `auth_time` are optional claims, only the exact `mfa` counts. The tenant is pinned by id on every binding; unbinding, set_sso_only and the exchange re-check the binding and the provider. The break-glass rule now covers disabling, offboarding, demoting, turning the app off, removing the last passkey and resetting the second factor; at the command line the same changes warn instead of refusing. A Microsoft-only account keeps its password hash (turning the flag off hands the password back), but `password_ok` now answers no for it, so the leftover hash proves nothing to change password, turn the app off, new recovery codes or any passkey re-authentication. Tests per path, and a store that cannot be read is never a yes. Hardening of the HTTP leg: Sec-Fetch navigation check, flow row with its own expiry, limits for start, callback, unknown-state callback and exchange, single-flight key and discovery fetches with a negative cache and a bounded number in the air, six second timeouts, throttled anonymous audit lines. CLI `--enabled/--disabled`, the stand-ins model Entra (scope, optional claims, keys without `alg`, a second key), 47 new Rust tests, 9 new web tests, a CLI test file, the web client and e2e updates, and the 12 mutants that survived the first review all die now (M6a, M7b, M9a, M10, M11b, M12c, M12d, M14b, M16a, M18b, E1, E2). Docs: D91 amended (auth_time rule for the later step-up, password_ok, rulings relayed), DEPLOY section rewritten, ISSUES section N rows N1 to N19, CLAUDE.md code map. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…t type
CI's `tsc -p .` failed on `import type { Playwright }` (no such export in
@playwright/test 1.64.1); the helper only needs `request.newContext()`. I had run
`vp check` and the specs but not `vp run typecheck` in this package after rewriting the spec.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ttle, call limits, race tests) - The test authority needs OBOP_CONNECTORS_ALLOW_LOOPBACK (1 or true), the setter is compiled only for tests, and the connector checklist warns when the variable is set. - Anonymous log lines: one per address (IPv6 by its /64) and kind in ten minutes whatever the text, 60 an hour for all, the provider's error cut to a fixed set of words. - Calls to Microsoft: the six-second limit applies from the connection on (name resolution is documented as outside it), single-flight waiters are capped, the permit is held for the call only, a 429 or 5xx from the token endpoint is "try again". - Tests: a code_take race with a barrier (8 racers, 300 rounds), barriers in the two-server and PostgreSQL races, replay steps that can fail, the 60 callbacks a minute limit, the binding_holds branches, rebind after unbind, discovery negative cache, slow provider, permits counted where they are made, key set single-flight. - The stand-in for the consent model is renamed to what it models (a request without the profile scope); docs say what Microsoft shows when consent is missing. - Wording: ngcmfa is Microsoft's own words, break-glass, unbind, change-password, sign-in-once, --trust-proxy, secret Value, the log lines, the connected apps. - Back button: the Microsoft button is usable again on a restored page and says where it goes. - Smaller items of the review: see docs/ISSUES.md N20. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… unknown kind in tests Found by re-running the mutations of the round on the committed source: the epoch re-read in the exchange, the reset of the count of lines left out, and the mapping of an unknown kind to oidc_error each survived. ISSUES N20 (j) lists the one that is not pinned. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The record cited "the mutation list in the pull request", which was not in it. The table is now in docs/ISSUES.md (section N, "Mutation checks of D91"): 71 source changes, each with the text replaced and the tests that fail, with counts for the race tests and the browser suite. One row survives and is listed (N20 j). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The wiring of the test authority is under test: the read of the process environment is injectable (authority_from_vars), the loopback flag is parsed by one shared function (obop_types::loopback_allowed, also used by the outbound client), and the real binary is spawned with and without the settings (the stderr lines are asserted). The anonymous lines of the log have three hourly budgets (begun sign-ins, callbacks that belong to no sign-in, sign-ins that went wrong) and the slot of a line is source, kind and sort (AnonLine), so a busy hour or noise cannot hide why a real sign-in failed. The waiting for a stuck fetch of the discovery document and of the key set is tested at its two call sites (the ninth waiter is told at once, a waiter gives up at the limit), and so is the place that the key-set fetch takes among the calls in the air. An unbind is tested for what it ends (sessions, phone apps) and what it leaves (agent tokens, MCP apps). A time claim that does not fit in 64 bits is no longer saturated. Wording of the missing-oid hint, the unbind and Microsoft-only log lines, the checklist item, the comments. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
D91 Decision 2 (the error code is mapped, not cleaned), Decision 5 and 9 (what Microsoft-only and an unbind end; the three budgets of the anonymous lines and what can be missing in a busy hour), the thread count (eighteen), the test authority (what is ignored with and without a line), the checklist item in CONNECTORS.md, the break-glass caveat on the lost-factor lines of DEPLOY and the site page, N7, N18, N20. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…dget Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ate they need, not for a guess of the time
The stand-in can hold a request until the test lets go of it, the server tells a
test how many requests wait for a fetch, and the tests of the eight places and of
the limit release the held call when the thing they pin has happened (eight wait
and the ninth was told; eight exchanges in the air and the late fetch refused)
instead of after 1.5 to 5 seconds. The limit of a call is shortened while the
exchange before it is in the air, so that it is the limit of the call under test
alone. The tests run with a 30 s limit unless they set their own. The check that no
JWT is stored looks for the dot before the payload ('.eyJ'): 'eyJ' alone turns up
by chance in the base64 of a hash or a sealed value.
Verified: 12 clean runs of the sso_tests group, and 6 more under 64 spinning
processes (the machine was at 99 percent before that); before this change one run in
three failed on a busy machine.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…g tests, 99 rows, all die Every row that a test of the sign-in binary kills was made again on cdc8c1f (the stand-in and the test world changed); the rows whose text moved were made on the earlier commits the table names. The three race-count rows, the three r2-h1 rows, the r2-thr rows and r2-jwks-no-permit are replaced as the section says. The section now also says how the timing tests wait and what that did to the failure rate. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The budget of every sort of anonymous line is pinned by one unit test over AnonLine::ALL (the table of decisions, a match without a wildcard, distinct words, every budget used), and the busy-hour test now also shows that a browser that is not the one that left, a redirect with no code and a token endpoint that is not well are written when the noise budget is used up. The cap of eight waiting requests is shown to be one cap for both fetches (four callbacks wait for the key set, four starts for the discovery document, the ninth is told at once), and the process-level test asserts one stderr line per case. D91 Decision 9 and DEPLOY count lines, not addresses, and say that a few addresses that play a flow of their own can use up the failure budget; Decision 3 names the one workspace edge of the manifests and the 64-bit bound of the time claims; Decision 4 names the phone apps and the MCP apps; the stale cross-reference in the key-set permit test is replaced. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ion table of 109 rows N20 (l) records that the budgets of the anonymous lines count lines, not addresses, and that a few addresses that play a flow of their own can use up the failure budget (a known limit, kept as built); N20 (m) lists the call sites that name a sort of line no test pins, with the mutation that showed it. The table gives the path of every file, says which rows repeat another row's change (109 rows are 107 changes), and holds the ten rows of the new tests; the seven rows whose killers changed in this round were made again. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
One conflict, in web/apps/backoffice/src/App.tsx: the import lines. PR 36 added describeFor and useI18n from @obop/ui, this branch added discardSsoReturn and restorePlace from ./api.ts; both sides are kept. Everything else merged without a conflict (Cargo.lock, CLAUDE.md, docs/CONNECTORS.md, docs/DEPLOY.md, docs/ISSUES.md, crates/obop/src/main.rs). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
PR 39 (the Google Drive browser) is in main, and the pull request
conflicted with it in the translation catalogues only:
web/apps/backoffice/locales/{en/messages.json, de|es|fi|fr|pl|sv/messages.json,
de|es|fi|fr|pl|sv/status.json}. Both sides only added keys, so each file
is the union: the same 1994 keys in every language (1984 here, 1979 there,
1969 in the base), no value differs and no side removed a key. en is in the
order of the extraction; the other languages keep this branch's order and
take the keys that only main added right after the key that precedes them
there; a status is "reviewed" when either side had it so. The diffs
against the branch are additions only. Everything else merged by itself
(Rust: obop-blob-gdrive, obop-http drive, the obop binary; docs; the
Drive browser in the back office).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Docs only; every sentence was read against the code. - N20 (m): the unpinned call sites are not all dear to pin. Discovery, TokenUnreachable, KeySet at its first fetch and SwitchedOff need one line of state each and were left out for time; Busy, Settings, KeySetUnusable, Begin and the second KeySet site (the fetch again for an unknown key) are really costly. No test asserts the line text of Settings, SwitchedOff, Busy, KeySetUnusable or Begin either. "The sort names are in one place each" is gone: three sorts have two call sites. - DEPLOY "What is recorded", D91 Decision 9 and N20 (l): a flow is spent by its first callback and writes at most one line; what is true is that an address can begin as many sign-ins as /start allows (30 a minute) and reach each of five failure lines once in ten minutes (a floor: Busy is a sixth), at least thirty an hour. "Up to six an hour" is per kind and sort. - N20 (l) and (k) no longer cite review ids that resolve nowhere in the repository (r3gb-1, F1, X3 of the second review); (l) points at fix round 4 in the description of pull request 37 and at the R2H-1 comments of the two tests. - D91 Decision 3: the obop-types edge is the only change of dependencies (the comment above the test-support feature of obop-auth was reworded too); a JSON null time claim counts as absent (a refusal for exp, "no expiry"; the same as leaving it out for nbf and iat). - Mutation table: the labels E1, E2 and M10 repeated issue rows of the same file; they are mt-E1, mt-E2 and mt-M10 now (no test comment or other document cited them). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This branch has not been deployed
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.
Sign in with Microsoft Entra: the OIDC core (D91, PROPOSED, item 3a)
Draft. Not for merge until the owner has ruled on D91. Nothing in this PR is ratified, and nothing was run against a real Microsoft Entra tenant, a real directory or a real phone: every test uses a stand-in for the provider that is in this repository.
What it does
One provider (Microsoft Entra), one tenant pinned by its GUID. Authorization code flow with PKCE S256 and the scope
openid profile(Microsoft sends the object idoidonly withprofile; the profile claims are ignored and never stored). The id token is checked by a verifier written onring(RS256 only, key bykidfrom the pinned tenant's key set,iss,tid,aud,azp,nonce,exp,nbf,iat,oid,amr) and dropped: no token of the provider is stored. No new external dependency (round 3 adds one edge inside the workspace:obop-authdepends onobop-types).oid; never an e-mail address or a user principal name. Only an administrator writes a binding (user_account.bind_sso,obop admin sso bind, REST); an unbound identity is refused and nothing is created. The first-bind rule is 3b and not built.sso_onlyaccounts ("Microsoft only") refuse the password and a passkey alone; a sign-in whoseamrcontains exactlymfais their second factor (D53 amendment;ngcmfaalone counts for nothing, on purpose). The account is made Microsoft-only only after one sign-in with the button on its binding, with the provider on and the binding in the provider's tenant, and with at least two other enabled administrators who sign in locally with a working second factor (break-glass). The same rule refuses every later change by a person that would leave fewer than two: disable, offboard, demote, app off, last passkey removed, second factor reset (at the command line these warn instead of refusing).password_okanswers no for such an account: change password, turn the app off, new recovery codes, and the passkey re-authentication (add, rename, remove, passwordless switch) are refused; a store that cannot be read isUnavailable, never a yes.OBOP_SECRET_KEY(re-sealed byobop secrets rotate). Security eventsoidc_*, limits per address (30 starts, 60 callbacks that spent a flow, 120 callbacks with an unknown state, 30 exchanges a minute), a negative cache, and the button and its messages in seven languages (six are machine drafts).sso_provider.configure|clear(listed everywhere, refuse agents when run, D41) anduser_account.bind_sso|unbind_sso|set_sso_only(agents propose), CLIobop admin sso status|configure|clear|bind|unbind|only,configure --enabled|--disabled.Fix rounds 1 and 2 (pushed earlier)
Round 1 answered the review of the first build (155 findings, 118 standing). Round 2 (head
95a2f30) answered the second review: the test authority gate (H1), the anonymous log (one line per source and kind, the provider'serrormapped to 12 codes orother), the calls to Microsoft (six seconds at each of three call sites, single flight, at most 8 waiters, at most 8 calls in the air, a 45 second negative cache), the mutation table committed indocs/ISSUES.md, and the lows.Correction of the first round's text (kept from round 2): the first round's body said mutant M11b (the result of the delete in
code_takeignored) was already killed. It was not; it survived until thecode_takerace test of round 2. It dies now (race counts indocs/ISSUES.md).Fix round 3 (pushed earlier, head
a1840ba)Answers the closure list of the third review (small, list-bound). What changed:
authority_from_vars(get)), the loopback flag is parsed by one function,obop_types::loopback_allowed(exactly1ortrue), which the outbound client ofobop-connectorsuses too (Config::from_vars); both are unit-tested, andcrates/obop/tests/sso_cli.rsspawns the real binary withenv_clearand asserts what it does with and without the flag, including the one stderr line. The checklist item says what is accepted (http://127.0.0.1:<port>orhttp://[::1]:<port>) and that any other value is ignored.r2-jwks-no-permit, dies now).AnonLine). The split protects the failure lines against begun sign-ins and against callbacks whose state is no flow of ours (unknown, spent or expired); it does not protect them against somebody who takes a sign-in of their own to its end (see round 4).test-supportcomment, N7, N18, N20 (a, b, g, j, k) with their true reasons.Fix round 4 (pushed, head
778524c)Answers the closure review of round 3 (no high; 2 medium that were one fault in two places, 3 low, 5 nit). Only tests and docs changed; no product source and no web file.
/startis open to anybody (30 a minute per address); a flow is spent by its first callback and that writes at most one line, but an address can begin as many sign-ins as/startlets it and reach each of five failure lines once in ten minutes (a callback without the cookie, a provider error, a cancellation, a redirect with no code, a code the token endpoint refuses; five is a floor,busyis a sixth), at least thirty an hour from one address, so a few addresses can use up the failure budget of an hour on purpose, and a refusal such as an expired client secret is then not written in that hour (the line that says the budget was reached and the next hour's count are). The split protects the failure lines against begun sign-ins and made-up callbacks, not against someone who plays a flow to its end. "Anybody who sends made-up callbacks" is gone from D91; the budget is said to be a fixed window. Kept as built and recorded as a known limit with its reason in ISSUES N20 (l); the budgets are not redesigned.AnonLine::ALL(sso_anon_testsinobop-commands) pins the budget of all 16 sorts (a table of decisions, amatchwithout a wildcard that makes the list complete, distinct words, every budget used;AnonGroup::ALLis used there). The busy-hour test now also shows that a browser that is not the one that left, a redirect with no code and a token endpoint that is not well are written after the noise budget is used up. The other call sites that name a sort (settings, discovery, switched off, busy, token unreachable, key set, key set unusable, begin) are not pinned: each is listed in N20 (m) with the mutation that showed it (eleven changes of a call site toNoFlow, all survive) and the reason.obop-authto the workspace crateobop-types) and the 64-bit bound ofauth_time(and ofexp,nbf,iat); Decision 4 says the phone apps end and the apps connected through the OAuth sign-in for MCP clients (D64) stay; "a callback with no valid state writes nothing" now says "no state, or one that is not shaped like a flow id"; "no new dependency" says "no new external dependency" in D91, Q64, N3; the stale cross-reference in the key-set permit test names its table row. The mutation table gives the path of every file (three crates have ansso.rs), says which rows repeat another row's change (109 rows are 107 changes) and no longer says allr2-h1-*rows were replaced.Merge of
main(pushed, heada4c3d00)mainmoved tocf317da(PR 36, the language switch, and PR 38, files in Google Drive) and the PR conflicted with it in one hunk, which stopped GitHub from running CI.mainis merged into the branch with a normal merge commit (no rebase, no force). The conflict was in the import lines ofweb/apps/backoffice/src/App.tsx; both sides are kept:Everything else merged without a conflict (
Cargo.lock,CLAUDE.md,docs/CONNECTORS.md,docs/DEPLOY.md,docs/ISSUES.md,crates/obop/src/main.rs). PR 36's source-reading guard over the web tree (i18nGuard.ts,i18nDeps.test.ts) passes on the files of this branch with noi18n-memo-okmarker of mine (the markers in the tree are PR 36's, inModelPage.tsxandmodules/view.ts). The new rows D8 to D14 ofdocs/ISSUES.mddo not collide with the N rows or the mutation table (the three namesE1,E2andM10that the mutation table shared with issue rows aremt-E1,mt-E2andmt-M10since the wording round below).Second merge of
main(pushed, head7a767ab) and wording corrections (pushed, head4c2b91c)mainmoved to9555614(PR 39, the Google Drive browser) and the PR conflicted with it again, in the translation catalogues only (web/apps/backoffice/locales/en/messages.jsonandde|es|fi|fr|pl|sv/{messages,status}.json, 13 files), which stopped GitHub from running CI.mainis merged in with a normal merge commit (no rebase, no force). Both sides only added keys, so each file is the union: 1994 keys in every language (1984 here, 1979 there, 1969 in the base), no value differs and no side removed a key.enis in the order of the extraction; the other languages keep this branch's order and take the keys that onlymainadded right after the key that precedes them there (the target-language files are in no sort order, and a re-sort would have moved whole files); a status isreviewedwhen either side had it. The diffs against the branch are additions only. Everything else merged by itself (the Rust of PR 39, the docs, the Drive browser of the back office).The coordinator's check of
a4c3d00found no high and no medium item and five wording items; they are answered in one docs commit (4c2b91c; no code, no test and no web file changed):Discovery,TokenUnreachable,KeySetat its first fetch andSwitchedOffneed one line of state each and were left out for time;Busy,Settings,KeySetUnusable,Beginand the secondKeySetsite (the fetch again for an unknown key) are really costly. No test asserts the line text ofSettings,SwitchedOff,Busy,KeySetUnusableorBegin. "The sort names are in one place each" is gone (three sorts have two call sites)/startallows (30 a minute) and reach each of five failure lines once in ten minutes (a floor,busyis a sixth), at least thirty an hour; "up to six an hour" is per kind and sortF1is another row there); (k) cited "X3 of the second review"test-supportfeature was reworded too); a JSONnullcounts as absent (a refusal forexp, nothing fornbfandiat, dropped forauth_time)E1,E2,M10repeated issue rows of the same filemt-E1,mt-E2,mt-M10(no test comment or other document cited them)Docs
docs/DECISIONS.md(D91 PROPOSED, the D53 amendment, D64 cross-reference, D33, relayed owner choices, Q64),docs/DEPLOY.md("Sign in with Microsoft"),docs/ISSUES.md(section N, rows N1 to N20, "Mutation checks of D91"),docs/CONNECTORS.md,docs/CONVENTIONS.md,docs/GLOSSARY.md,docs/spec/security-privacy.md(decision id range only),CLAUDE.md(code map), the site page on two-factor authentication. The OR rows are untouched.What I ran (Windows 11)
On the first merged head
a4c3d00(the second merge changed only the translation catalogues and files of PR 39; the rows marked * were run again on it):vp testinweb/apps/backoffice*mainalone has 339), including PR 36's guard over the whole web tree (also on7a767ab, where the guard reads PR 39's Drive browser too)vp checkinweb/apps/backoffice,web/packages/ui,web/packages/client,web/packages/e2e;vp run typecheckinweb/packages/e2e;vp run i18n:checkinweb/packages/ui*catalogues ok(on7a767ab: 160 and 60 files formatted, no lint errors; 1994 keys in every language)cargo check -p obop-http -p obop --tests*7a767ab: finished, no errorcargo fmt --all --check;cargo clippy --workspace --all-targets -- -D warningscargo test -p obop-auth -p obop-httpcargo test -p obop --bin obop sso_tests;cargo test -p obop --test sso_cliobop modules check;obop openapi --out ...thenvp run generate:typesdistanddist-adminrebuilt (dist-adminfrom PowerShell), debug binary rebuilt; Playwrighttests/sso.spec.tsandtests/language-switch.spec.tswithOBOP_E2E_KEEP=1Not run on the merged tree: the whole Playwright suite (CI runs it), the whole Rust workspace (the Rust that
mainchanged is the new crateobop-blob-gdriveand edits inobop-commands,crates/obop(main.rs,ops.rs,storage_cli.rs),Cargo.tomlandCargo.lock;clippy --workspace, the build and the test groups above cover the merge), PostgreSQL, and theobop-storagetests.Before the merge, on
778524c:cargo test --workspaceoutsideobop-storage1995 passed, 0 failed, 5 ignored;obop-storage278 passed and 4tls_testsfailed because their own postgres:17 container did not start in 60 s on a machine at 99 percent CPU (not touched by this PR; they passed in round 3, 2274 passed in all). Ona1840ba(round 3):cargo test -p obop-auth --libagainst postgres:18 224 passed; the whole Playwright suite 236 passed, 16 skipped, 1 failed (polish.spec.ts, a 30 s timeout under load, passes 15 of 15 alone).CI on
a4c3d00(the first merge): green, run https://github.com/SpaceCorps/obop/actions/runs/41338849214. CI on the head4c2b91c: green, run https://github.com/SpaceCorps/obop/actions/runs/41351132873 (lockfiles,web,rust,e2eandallpassed on the first attempt; no re-run was needed).mainis9555614(PR 39) and GitHub reports the PR mergeable (PR 40 is open and edits the D section ofdocs/ISSUES.md; this branch edits section N and the mutation table). The PR stays a draft.Mutation checks. 109 rows in
docs/ISSUES.md(107 different changes), each one change to the source, the covering tests run, the file put back byte for byte: 109 die, 0 survive; none dies only because the tests do not compile. Each row names the commit it was made on; the ten rowsr4-...are for the new tests (for exampler4-group-nocode-is-noise, the x06 of the review, dies inthe_list_of_sorts_holds_each_sort_once_and_each_sort_has_the_budget_that_was_decided;r4-waiting-cap-per-fetch, a second counter for the key-set waiters, dies in the new shared-cap test alone;r4-stderr-line-twiceis x04b), and seven rows whose killers changed were made again. Eleven more changes (a call site of an unpinned sort moved toNoFlow) were made to show what no test pins: all eleven survive and are listed in N20 (m), not in the table. Race counts are unchanged from round 2.NOT verified
amrandauth_timeunder real policies, admin consent behaviour including the admin consent workflow, discovery, key rollover, error texts).pageshowevent, not a real back/forward cache restore (N20 e).obop-auth; theobop-storagetls_testsin this round.For the owner
D91 is PROPOSED. Open decisions are in ISSUES section N: N12 (retention and data class of the object id in the log), N16 (certificate credential), N3 (the verifier on
ring, relayed and closed, can be asked again).🤖 Generated with Claude Code