Skip to content

fix(ui-server): move account administration commits to write worker - #3024

Merged
schlessera merged 6 commits into
mainfrom
fix/3002-account-admin-offloop-044
Oct 26, 2026
Merged

schlessera merged 6 commits into
mainfrom
fix/3002-account-admin-offloop-044

Conversation

@schlessera

@schlessera schlessera commented Oct 26, 2026 •

Copy link
Copy Markdown
Owner

Closes #3002
Refs #2850 (epic)

What changed

Account and security administration commits on the shared SQLite write worker at synchronous=FULL: delegated principals and revocation, passkey counter/registration/label/deletion, reference grants, push subscribe/unsubscribe/unbind and lazy VAPID creation, and the account partition seed. Routes answer after their required commits. Client disconnection remains immediate after durable revocation, and revocation HTTP responses also await push unbinding.

Why this shape

Follows the SQLite write-worker decision and #2992's principal-write pattern. Existing principal/grant transactions run unchanged on the write connection. Passkey deletion retains its two autocommits in order: credential deletion, then credential-session revocation. Authority checks stay with the writes they authorize, and refusal classes and HTTP statuses survive the worker boundary.

The partition seed commits before the hosted account-key reader is constructed. Push lifecycle SQL runs on the shared queue; racing first-use callers read back the same stored VAPID pair. Standalone helpers retain their inline behavior. Runtime expiry observes unbind failures without creating an unhandled rejection.

Includes a patch changeset for @schlessera/brain-ui-server. No supported public API or machine-contract change and no CONTRACT: commit. The awaitable host/activity wiring is internal under the #1053 WsHost classification. Existing source citations are updated wherever this slice moves their targets; the ui-react change is solely such a comment citation.

Proof

Current head: 9b64841d58fb02d23292d1e864fbfc90bd226e97.
Main merged before the refreshed heavy runs: 64af9b5a0803f6c890c9660d180a3c46a9a00ac0.

  • Refreshed focused account + WebSocket revocation proof: 62 tests, 327 assertions, zero failures.
  • Refreshed bunx --no-install tsc --noEmit, bun run lint, and bun run check:pr --base origin/main: pass. The preflight reports 809 pass / 5,214 assertions across 33 files; all 1,342 source citations validate.
  • Refreshed connection mutations: individually route all 14 procedures (createPrincipal, revokePrincipal, revokeAll, createGrant, revokeGrant, registerPasskey, passkeyCounter, labelPasskey, deletePasskey, partitionSeed, vapid, subscribe, unsubscribe, unbindPrincipal) to the app connection. All 14 selected tests fail with APP_CONNECTION_WRITE on their named route assertions or guarded write calls.
  • Refreshed durability mutations: individually lower those same 14 procedures to synchronous=NORMAL once the probe is armed. All 14 selected tests fail with SQLITE_WRITE_BELOW_FULL. All mutations restored, then the focused suite passes.
  • Refreshed bun run test ./tests: 1,966 pass, zero failures, 74,436 assertions across 179 files (798.34 s).
  • Refreshed provider-history guards: 31 pass, 259 assertions. Packed-consumer smoke: every publishable package packed, installed and passed Bun/Node probes, including the React 18 consumer.
  • Refreshed full native hosted matrices: four healthy cells and the expected negative control pass without load (433.007 s) and under three fsync writers (448.153 s; 811 / 815 / 815 completed writer iterations, zero writer failures). All ten fixture runs report no mounted filesystem, attached/created loop device, backing file or cleanup errors. AppArmor is unavailable, so no AppArmor qualification is claimed.
  • Refreshed bun run test packages/ui-server: 4,168 pass, zero failures, 28,751 assertions across 351 files (2,502.89 s); exit 0.
  • Current-head draft checks: contract, sync, draft gates, changeset pass. Expensive hosted categories are skipped as expected for a draft; selected ready-candidate proof remains with the coordinator.

Exact refreshed per-procedure mutation receipts follow. Each row was run once through the app connection and once at NORMAL, exited 1 on the shown assertion/refusal, and contains APP_CONNECTION_WRITE or SQLITE_WRITE_BELOW_FULL respectively. None is a module-load failure. Restored focused proof exits 0.

Procedure App-connection mutation: failing assertion NORMAL mutation: failing assertion
createPrincipal ACCOUNT_MINT_COMMITTED_BEFORE_HTTP_RESPONSE ACCOUNT_MINT_COMMITTED_BEFORE_HTTP_RESPONSE
revokePrincipal ACCOUNT_REVOKE_COMMITTED_BEFORE_HTTP_RESPONSE ACCOUNT_REVOKE_COMMITTED_BEFORE_HTTP_RESPONSE
revokeAll ACCOUNT_LOGOUT_OWNER_HTTP_AWAITS_COMMIT ACCOUNT_LOGOUT_OWNER_HTTP_AWAITS_COMMIT
createGrant ACCOUNT_GRANT_COMMITTED_BEFORE_HTTP_RESPONSE ACCOUNT_GRANT_COMMITTED_BEFORE_HTTP_RESPONSE
revokeGrant ACCOUNT_REVOKEGRANT_COMMITTED_BEFORE_HTTP_RESPONSE ACCOUNT_REVOKEGRANT_COMMITTED_BEFORE_HTTP_RESPONSE
registerPasskey PASSKEY_REGISTER_HTTP_AWAITS_COMMIT PASSKEY_REGISTER_HTTP_AWAITS_COMMIT
passkeyCounter PASSKEY_COUNTER_HTTP_AWAITS_COMMIT PASSKEY_COUNTER_HTTP_AWAITS_COMMIT
labelPasskey PASSKEY_LABEL_HTTP_AWAITS_COMMIT PASSKEY_LABEL_HTTP_AWAITS_COMMIT
deletePasskey PASSKEY_DELETE_HTTP_AWAITS_COMMIT PASSKEY_DELETE_HTTP_AWAITS_COMMIT
partitionSeed APP_CONNECTION_WRITE settings SQLITE_WRITE_BELOW_FULL
vapid APP_CONNECTION_WRITE vapid_keys SQLITE_WRITE_BELOW_FULL
subscribe APP_CONNECTION_WRITE push_subscriptions SQLITE_WRITE_BELOW_FULL
unsubscribe APP_CONNECTION_WRITE push_subscriptions SQLITE_WRITE_BELOW_FULL
unbindPrincipal APP_CONNECTION_WRITE push_subscriptions SQLITE_WRITE_BELOW_FULL

Original failing-first receipts: the unmodified administration/push routes yield nine APP_CONNECTION_WRITE failures plus the named revocation-response wait failure; the original passkey routes separately yield all four APP_CONNECTION_WRITE passkey_credentials failures. No module-load failure is counted.

Additional retained mutations on the same administration implementation: removing the revocation commit await fails ACCOUNT_REVOCATION_HTTP_WAITS_FOR_COMMIT; removing the runtime-unbind await fails ACCOUNT_REVOCATION_HTTP_AWAITS_PUSH_UNBIND_COMMIT and ACCOUNT_UNBIND_REFUSAL_HTTP_STATUS; removing refusal revival fails ACCOUNT_GRANT_DUPLICATE_CLASS; returning an uncommitted VAPID public key fails PUSH_VAPID_RACE_SHARES_PUBLIC_KEY. Restored tests pass. The held-worker test verifies every authentication after the response rejects the revoked principal; another verifies an unbind failure retains HTTP 500 after durable revocation.

Earlier receipts on 21f820efc6ebac1fad92082db8cd8a636ebd2c6b, based on fd51cadf42918202fe3a68e0333ffdcecbdbcb8d, are retained with their original identities. That head passed root tests (1,966 tests / 74,416 assertions), provider-history guards (31 tests / 259 assertions), every packed Bun/Node consumer probe, and both complete native matrices (472.059 s without load; 506.453 s under three fsync writers, with [772, 776, 773] successful writer iterations and complete owned cleanup).

Its first full ui-server run completed with 4,144 passes, four failures and 28,460 assertions in 3,585.36 s. The failures are the boot-prune timeout and following mount-timer residue (#3020), recovery cleanup completion (#3021), and the known budget fixture race (#3009). The first two pass in isolation on both old base and old head (2 tests / 122 assertions each). Full old-base/old-head comparisons were stopped through their owned unit-test wrappers when the coordinator requested this main refresh; both exited 143 and are recorded as superseded, never passing proof. No native proof was interrupted.

All heavy commands use the shared host slot/headroom wrapper. Native commands use the exclusive fixture lock without a heavy slot. Every command retains its head/base/tree, exit and complete log in durable receipts. The earlier native matrices provided no AppArmor qualification because AppArmor was unavailable.

Native lag coverage: the pi UID 1000 admission cell makes no administration, logout, passkey, reference-grant or push requests and has no registered push destination. The account seed and the fixture's raw principal creation occur before ready. The cell cannot supply post-ready before/after lag attribution for this group, and no latency improvement is inferred from it. Real-worker connection/FULL guards, slow-queue revocation, HTTP/refusal tests and concurrent VAPID delivery/key-route races provide targeted behavioral evidence.

Checklist

  • Focused local preflight/behavioral receipts and complete selected hosted proof passed; actual head/base/job checkout evidence is in Proof.
  • Acceptance criteria from the issue are all met, or the gap is named below.
  • The PR and completed/advanced issues have verified release milestones under docs/process/github.md.
  • A changeset is included, or this changes nothing a consumer can see.
  • No personal data anywhere in the diff — the leakage gate covers the whole tree.
  • The integration contract index or relevant component is updated in this same commit, or nothing in the machine surface moved.
  • Docs under docs/ reflect the change, or nothing documented moved.
  • Provider history stays append-only (docs/decisions/provider-history.md): the provider-history guard passes, or nothing on a turn's path changed.

Anything left open

Draft only. All required local proof passes on the merged head. All four earlier failing cases pass in the refreshed full suite; because the base changed, this is non-reproduction, not causal attribution or a claimed repair. Earlier observations remain recorded under #3020, #3021 and #3009. The coordinator owns candidate confirmation, ready status, and merge. Native post-ready lag attribution does not cover these administration requests, as explained above.

Main advanced during the completed proof to da1ec5f5d8e6d050a95f542112c5eed22de694a5 (#3025, #3026). These commits are not included in the qualified head above. A read-only merge assessment finds source code merges automatically and overlaps in three documentation files: docs/decisions/session-principals.md, docs/decisions/sqlite-write-worker.md, and docs/plans/async-collaboration.md. Pending integration work is to reconcile those citations/prose with the final reference-source slice, then request qualification for that resulting candidate. No combined-tree proof for this later main is claimed.

@schlessera schlessera added this to the 0.44.0 milestone Oct 26, 2026
@schlessera schlessera added type: fix Something is broken and must behave the way it is documented. area: ui-server Hono app factory: turn coordinator, auth, session catalog, routes, cron. labels Oct 26, 2026
@schlessera schlessera self-assigned this Oct 26, 2026
…n-offloop-044

# Conflicts:
#	docs/decisions/http-api-boundary.md
#	docs/decisions/session-principals.md
#	docs/decisions/sqlite-write-worker.md
#	docs/plans/async-collaboration-requirements.md
#	docs/plans/async-collaboration.md
#	docs/plans/navigation-recovery.md
# Conflicts:
#	docs/decisions/session-principals.md
#	docs/decisions/sqlite-write-worker.md
#	docs/plans/async-collaboration.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: ui-server Hono app factory: turn coordinator, auth, session catalog, routes, cron. type: fix Something is broken and must behave the way it is documented.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ui-server: account and security administration commits on the write worker

1 participant