Repository navigation
Develop - #1129
Merged
Merged
Develop#1129
Conversation
…wn: behind the balancer the second poll can land on a node that has not yet replicated the first poll's lastPolledAt (cluster mode, run #5).
…the first GET /admin-api/scheduler after a realm's removal never answered and the job was killed silent at 300 s; the next hang names its request.
). Past risk.populationUsersCacheS the held count is answered and counted again in the background; only a realm's first count is waited for. On 8081 the sign-in that found it due waited 9.6 s for a cold count of a million rows. A failed recount keeps the held number. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ot its lane (#1076). It was drawn from the lane alone (#643), which is `pqc` only while webauthn.pqcAlongsideClassical is on (off by default), so an ML-DSA passkey said "classical" beside an Algorithm row saying post-quantum. A post-quantum key in the post-quantum lane keeps its second-account note. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…st by default (#1078). webauthn.algorithms' default puts the strongest parameter set first; every other algorithm keeps its place. POST_QUANTUM_ALGORITHMS, the list a post-quantum-only or pq-first request offers when its list names no ML-DSA, follows the same order. env/defaults.js and the docs follow the table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ortal were the only receivers. `node env/generate_defaults.js` over develop. The internalReceivers and actOnSignalsInDevelopment comments are the table's labels since #1042 made reports a third receiver; the workers group's comment column is realigned. No value changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…). An AAGUID, AAID or attestation certificate key identifier, its kind told by its shape, answered by RiskDatasets.describeAuthenticator(): the model, certification, latest status, compromised, status reports and statement, or why nothing is listed (not-a-key, all-zero, no-blob, not-listed, unreadable). A stale BLOB is still asked and marked stale. The page's second lookup form and GET /admin-api/risk?authenticator= (rule 7), as the view's authenticatorLookup. Nothing that decides reads it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The tab's label is its section heading, which still said "Look up an address" after #1080 put the authenticator lookup beside it. The heading is now Lookups, and each form has a subheading: lookup.address.heading (the old text and its translations) and authenticator.heading. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both lookups are GET forms whose action was bare /admin/risk. The console runtime navigates a GET form to its action's fragment, and the tab drawn is the :target, so with none the answer opened on Metadata. Both forms now go to /admin/risk#risk-tab-lookup, protocolTabs()'s id for the section; tests/risk_realm_admin.js C3 holds both in that tab and returning to it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…portal and reports (#1086). Server configuration → Broadcast Announcement (/admin/broadcast) and its rule 7 twin POST /admin-api/broadcast/send: a message, the applications (all three ticked), and for a service administrator in the default realm this realm or every realm. It travels as this service's own sts-family SET, urn:iya:sts:secevent:event-type:announcement, on the three internal receiver streams CAEP and RISC already use (they now ask for it; a stream seeded earlier is made to ask at the next seeding), and only on those. Each surface's receiver collects it over /ssf/poll as before and keeps a verified one naming it for ssf.announcementKeepS. Every open tab asks its own API every ssf.announcementPollMs (/admin-api, /account-api, /report-api /announcements): the first ask fixes the tab's start, so only what arrives later is shown. One browser module, common/web_announcements.ts, draws the banner for all three: the floating flash shape, text only, an ×, closed after ten seconds; it asks through sendOnce so a background poll never begins a sign-in. Settings ssf.announcementPollMs, ssf.announcementKeepS, ssf.announcementMaxChars; STS-SSF-0153..0155; catalogs and prose catalogs in English and six translations; docs/shared-signals.md; ssf/CLAUDE.md. tests/ssf_announcements.js in process, and tests/protocol/sts_broadcast_announcement.js (local) over HTTP. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1088). On 8081 a broadcast answered "Nothing was sent": broadcastAnnouncement() selected streams by record.internalSurface, the half of a stream record a persisted round trip can lose (ssf/CLAUDE.md, One stream per surface), which is why streamFor() asks for the derived id first. It now asks streamFor() for each chosen surface, and when nothing is sent the answer names each surface's reason: no stream, disabled, or does not take the event. tests/ssf_announcements.js C2 (marker removed, still sent) and C3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…uncement event (#1091). After #1088 a broadcast on 8081 answered "does not take the announcement event" for all three surfaces. seedStreams() asked for the type as the module loaded, before the store's copy of the streams was read, so the stored record without it was the one kept - moveToPoll()'s trap. askForAnnouncements() is now asked by pollAll() with the store open and by broadcastAnnouncement() just before it selects, and checks the requested and delivered lists separately. tests/ssf_announcements.js C1b, C1c. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ust realm (#1090). rcbj: the Signals tab served no purpose. The tab, drawSignals() and its sixteen report.signals* catalog keys go; Refresh moves from the page bar to the top bar, after the Trust realm field. The receiver stays (CAEP/RISC reactions, #1086 announcements), and GET /report-api/signals stays as an API; its inboxPath, the ssf.internalReceivers description (and its prose catalog translations), docs/signals-received.md, docs/shared-signals.md, report/CLAUDE.md and ssf/CLAUDE.md now name the API, not a page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ore asserting the shared secret: behind the balancer the draw can land on a node that has not yet pulled the started enrolment's pendingTotp row (cluster mode, run #6).
…inct jti, not by arrival: a lease-claimed delivery may POST the same token twice (section 2.6 deduplicates), which run #6 met in cluster mode.
A browser receiver (rcbj/oidc-caep-client) creates its stream with the person's token and deletes it at sign-out; a tab closed for good left it for ever (ssf.inactivityTimeoutS is 0 by default, for push receivers), and a person who kept losing the stored id reached ssf.maxStreams. A stream a person owns and collects by poll now takes ssf.personStreamInactivityTimeoutS (86400) and ssf.personStreamInactivityAction (delete) when neither its application nor ssf.inactivityTimeoutS sets a timeout; push streams are excluded. inactivityTimeout()/inactivityAction()/isPersonPollStream() in ssf_streams.ts; the maintenance job and its off() use them. Settings, prose catalogs in six languages, docs/shared-signals.md, ssf/CLAUDE.md, tests/ssf_person_streams.js section 7. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mon/ and tests/CLAUDE.md (the deferred allow-list sentence removed, the captured-registration fixture format written down), and the two settings' prose in every full settings catalog.
…s capabilities ride on client after a '|', so audit.js's cap no longer drops the attestation field, which carries the enterprise downgrade.
…instead of being skipped after one stale read (#1118). Run #7's cluster mode skipped a new realm's first minted change (audit.nums) on node-b under STS-STORE-0089 for a key node-a had made 0.8 s earlier. The one read of the data-key rows could be a refresh already in flight, sent before the key's row committed, or prefetch()'s page read, which marked the key unheld. keystore.refreshDekRows({ fresh: true }) now waits out a read in flight and reads again. persistence_minted.js's waitForDek() reads up to five times, 200 ms apart, one wait per key shared by its changes (the directory applier's keyedRetries shape), and only then notes the key for the two-second skip. prefetch() no longer notes a key unheld. STS-STORE-0089 now means a key still not held after the reads. tests/minted_unheld_dek.js U5 and U6; UNHELD_READS = 1 fails U3, U5 and U6. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd keeps webauthn.passkeyHistoryDays of them; the passkeyHistory source in /report reads them, with three predefined queries (#1109). The passkey report's recount, right after it writes the baseline, writes passkey_report.history keyed by the UTC day: people by kind, passkeys by algorithm, by lane and by attestation algorithm, and each named passkey profile's population, bounded as the profile filter is and at most twenty-five profiles. The same run removes the rows older than the new setting webauthn.passkeyHistoryDays (400 by default). A row that cannot be written or pruned is logged (STS-AUTHN-0520, 0521) and fails nothing; a profile whose population cannot be read is recorded as not counted (STS-AUTHN-0522). The store is the cell tier's. report/passkey_history.ts projects each stored day into rows told apart by scope (realm, profile, algorithm, lane, attestation); the catalog's passkeyHistory source carries three presets: post-quantum adoption by day, passkeys by algorithm by day, adoption by passkey profile by day. tests/passkey_history.js holds the row, its retention, the source and the presets, in process and over /report-api.
docs/passkeys.md: the move over time, read in /report, and the setting. docs/reporting.md: the passkeyHistory source, its scopes, fields and predefined queries. common/CLAUDE.md 3cs argues writing the row in the recount rather than a sibling job; report/CLAUDE.md the one-row-per-scope projection; cluster/CLAUDE.md's job table and tests/CLAUDE.md's row. persistence/CLAUDE.md records that the tier scan reads literals only, so the passkey report's two earlier stores are unlisted and sealed for the cell tier by the unclassified fallback.
…tings catalog, and the adoption preset's limit at the synchronous cap so it answers at once (#1109).
…duration, and a device link writes one (#1108). Each door hands webauthn_ceremonies.record() the application in play and the instant its pending ceremony's options were issued; durationMs is measured from that on the server (the step's optionsAt, the screen's passkeyChallengeAt, a pending enrolment's or upgrade's issuedAt, an upgrade's provenAt, a device challenge's issuedAt). The row was already thirteen fields, so audit.js's cap had been dropping the attestation: the attachment, transports and attestation now fold into `authenticator`, and postQuantum, a function of the algorithm, is no longer stored. unfold() reads both the new and the older shape back. The device link (device_enrolment.finishLink()) wraps its check and writes the row, door device-link. tests/webauthn_ceremony_log.js covers all three.
…rows, with three predefined queries (#1107). One row per webauthn.ceremony and webauthn.algorithm-mismatch audit row still in the ring, read as authActivity reads it (the fresh() wait, the audit.maxEvents cap) and projected by report/passkey_ceremonies.ts: the door, outcome, client, error name, offered order and the client rule after its first bar (#1112, offerRule), the algorithm used and whether either is ML-DSA, the application and server-measured duration, and the authenticator facts unfolded from either row shape. Nothing is indexed: the ring is a list in memory and the read is bounded by it. Its three predefined queries are #1110's presets: ML-DSA registrations that failed by client, registrations that asked for ML-DSA and made a classical passkey by client, and classical sign-ins by people who hold an ML-DSA passkey. The query form now draws a saved or preset boolean filter. The catalog has thirteen sources.
… and how to find out for a realm (#1113). A section of docs/passkeys.md for operators planning the move: the five kinds of failure (a whole registration failing instead of falling back, the offered order not honoured, a security key with no PIN, payload size on tight transports, slower large parameter sets), what each looks like on the ceremony row, what the service does about each (the classical retry, #1112's client rules, the registration modes and the offer's order, list-and-refuse), and the passkeyCeremonies report source as the way to measure a realm. The dated compatibility table it replaces named products, versions and outside reports, which go stale; no such list remains.
…nt apart from capabilities a #1114-shaped row carried on it, and the docs describe the attestation inside authenticator (#1107, #1108). #1114 met the same twelve-field cap and moved the capabilities onto client after a bar; the two folds are reconciled into #1108's shape (client plain, capabilities its own field, the enterprise downgrade leading the attestation part of authenticator). unfold() and the passkeyCeremonies projection read a row written either way.
…ys, with a section on post-login activities (#1122). The short pointer to post-login activities becomes a walk-through: who is asked, how the policy is targeted and turned on, what a person sees at sign-in (the methods, Skip and the allotted time, the classical fallback, a device that cannot make ML-DSA), the mail reminders, and the three report sources. Requiring a post-quantum attestation moves beside requiring a post-quantum passkey. Stale text goes: the default algorithm order since #1078, the upgrade paragraph that predates the post-login method, the refusal page's link to an upgrade, and the duplicated door list. The lanes row, the post-login policy and its operations, and the missing passkey error codes are added to the page's tables.
…-list pages, and the passkey reference brought up to #1100-#1114 (#1122). The authentication page gains a short section on a sign-in held for a post-login activity, the passkey policy's lanes row, the post-login step as a place a passkey is registered, and the two WebAuthn settings its table lacked. The console's Policies section names every policy kind and describes the post-login activity policy; the page list has /authn/post-login. The post-login page says what an administrator can do for a device that cannot make ML-DSA, that the attestation rule always adds the passkey alongside, and where the reminder's link goes.
…is a post-login activity, and its groups are chosen by search, several at once, their union mailed (#1123). ONE CONCEPT. common/post_login_activities.ts (was post_login_campaigns.ts, class PostLoginActivities) keeps a realm's started post-login activities in post_login.activities and what each run did in post_login.activityRuns; the scheduler job is post-login.activity-mail; the audit actions are admin.post-login-activity.change and authn.post-login.activity-run; the operations are /admin-api/post-login-activities and its start, pause, resume and end (mgmt-api/post_login_activities_api.ts); the console page is Directory → Post-login activities (/admin/post-login-activities, admin-ui/web_post_login_activities.ts, consolePostLoginActivities) with the tabs Activities and Start an activity. A record is a StartedActivity (id pla-…); the person's progress row and the postLoginProgress report column that name it are mailedBy. The post-login activity policy kind on Directory → Policies is unchanged: it is the rules a started activity follows. No migration: no released data held a row under the old store names. SEVERAL GROUPS, THEIR UNION. A start takes `groups`, a list of cns or DNs (at most fifty), each resolved through the group index; one the realm does not have is refused by name with the new STS-AUTHN-0530. ldap_server.js's groupMembersAfterAsync() now takes a list and walks the UNION of the groups' member keys with one cursor, so a person in two groups is read and mailed once. The duplicate check compares the set of DNs. CHOSEN BY SEARCH. The start tab draws kit.chooserPane() from a slice the view cuts (groupChooser: twenty from the clamped groupfrom, the chosen left out), asked of the group index through the new groupChoices() and groupsNamed(), never the directory's people. A result is a link adding its group to the page's own `groups` query parameter (one DN a line), each chosen group has a Remove link, and the form carries one hidden `groups` per group. The GET operation takes groupq, groupfrom and groups and answers groupChooser (rule 7). Every catalog in the seven languages, the error-code text, the sts_metadata descriptions, the tests (renamed, with the union, the de-duplication, the chooser's slice and paging, the API's list and its refusal) and each directory's CLAUDE.md follow.
…e group search and its list, the union of the groups, STS-AUTHN-0530, the API's groups, the report's mailedBy (#1123).
…hod does to the classical passkey, STS-AUTHN-0475 and 0309 as the code checks them, who can register a passkey for a person, and a dead heading link (#1123). * post_login_policy.ts: the activity field said the classical passkey is kept; under `method: upgrade` it is replaced in place. It now says the method decides (the policies prose catalog in seven languages). * STS-AUTHN-0475 blamed webauthn.pqcAlongsideClassical/pqcOnly; the code asks canRegister(): passkeys on, the role allowed, an ML-DSA algorithm offered (the default list has them), the post-quantum lane not refused, and for an upgrade the upgrade not refused. * STS-AUTHN-0309 also names an unreadable passkey-policy clientRules rule. * authn.postLogin.failedHelp no longer promises that an administrator can register a passkey: only where the person's passkey policy allows administrator enrolment, or by an upgrade from the console (seven languages). * docs/risk-scoring.md and docs/xacml.md point at signals-received.md's "What the three applications do with a signal".
…with ';', since a query value may hold no control character and the line break was refused at the gate (#1123). RFC 4514 escapes a ';' inside a DN value, so groupNamesOf() splits on an unescaped one only.
…sign-in, not only by mail (#1126). A started activity turns the sign-in invitation on for its population, the union of its groups by DN or the whole realm, under the rules of the profile the post-login policy selects for each person; only `enabled` is overridden. A named profile switched off still excludes its members; `default` off excludes nobody. Paused or ended, the activity asks nobody, and a person the policy itself enables is still asked. The realm's running activities are read from the started store each process already holds (at most fifty rows), and membership from the person's own groups through the directory's personGroups() hook, asked once per question. The progress row records who asked (`askedBy`: policy, or the started activity's id) at the ask, Skip and done, never at a mail, and the postLoginProgress report source shows it. The console page says what a started activity does at sign-in and by mail in all seven languages and drops the policy-off warning; the API descriptions, the docs and the CLAUDE.md files follow. tests/post_login_started.js holds the selection in process.
…e as a refusal-close race: OpenSSL's unexpected_message alert and node's close reached tlsfuzzer the wrong way round once on the debugger listener (run #10 of 2026-10-28, memory mode), the race the kit already names for two other probes.
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.
No description provided.