Skip to content

Promote tested TST Milkdown, collaboration, and MFA changes to PRD - #16

Merged
Che4ter merged 21 commits into
bv-mainfrom
promote/tst-milkdown-mfa-collab
Aug 11, 2026
Merged

Promote tested TST Milkdown, collaboration, and MFA changes to PRD#16
Che4ter merged 21 commits into
bv-mainfrom
promote/tst-milkdown-mfa-collab

Conversation

@gru3zi

@gru3zi gru3zi commented Aug 11, 2026

Copy link
Copy Markdown

Promotes the 20 tested TST-only commits from bv-develop onto current bv-main without conflicts.nnIncludes Milkdown/unified-editor migration and description UI fixes; collaboration stability, note rename persistence, large-note protection, direct export, and Kusto blocks; local password MFA feedback and lockout hardening; and API/auth/socket error handling fixes.nnValidation: clean merge from the shared merge-base and git diff --check passed. The PRD Azure pipeline will compile the container before deployment.

Marc Schlaeppi and others added 21 commits July 15, 2026 03:20
The /collab/ resolver was hardcoded to Docker's embedded DNS
(127.0.0.11), which doesn't exist under podman/netavark where
aardvark-dns listens on the network gateway instead. This silently
502s every /collab/ request after a 30s timeout on podman stacks.
Read the real nameserver from the container's own /etc/resolv.conf
at startup instead.
Clients occasionally join a socketio room before a case is selected
(e.g. "case-null"), producing a non-numeric id. That's a premature
join, not an authorization bypass attempt, so it should be denied
gracefully rather than raising and killing the event-handler thread.
response_api_error() ignored its second positional status_code
argument and always returned HTTP 400, so callers passing 401/403
were lying to clients about the actual error. Add a proper
status_code keyword param and update call sites to use it.
The lockout check only applied when the session's tracked
mfa_fail_user_id matched the user currently logging in, so an
attacker could clear another account's active lockout early simply
by logging in as a different user first. Enforce the lockout
regardless of which user triggered it.
Clicking a note re-entered note_detail() while a previous call was
still mid-flight, so two overlapping calls both passed the teardown
check before either nulled note_split, both created a fresh
SplitEditor, and whichever resolved last silently overwrote
note_split -- orphaning the loser's instance (websocket, DOM nodes)
with no destroy() ever called. Add a per-call token checked after
every await so a superseded call detects it lost the race and tears
down what it already created. Also defensively remove any stray
collab bar/badge left behind by an orphaned instance on create().
transformOutsideCode() ran its transform over the remaining tail of
the document unconditionally, including text inside a fence that was
never closed. Detect an unterminated opening fence and leave it (and
everything after it) untouched.
… editor

- String.replace() treats a literal "$" in the replacement as a
  special pattern (e.g. "$&", "$1"), so inserting selected text
  containing a "$" into a snippet silently mangled it. Pass a
  replacer function instead.
- Look up code languages via milkdownCodeLanguages (which includes
  Kusto) instead of the unregistered @codemirror/language-data list.
- destroyPrevious() is called fire-and-forget from a sync
  constructor, so this.crepe may still be null while initialize()
  is pending. Await this.ready first so we always destroy the Crepe
  instance instead of leaving initialize() to mount into an element
  a newer instance already took over.
pycrdt's Rust objects are !Send; the automatic, threshold-triggered
GC could collect a Doc/Subscription on a different thread than the
one that created it, crashing with a pyo3 unsendable-object error.

- gunicorn (business/collab.py): route all pycrdt calls through a
  single dedicated worker thread (iris_engine/collab/pycrdt_worker.py)
  and disable automatic GC process-wide, collecting explicitly only
  on that thread.
- collab_server.py (uvicorn, single event loop thread): add a
  periodic gc.collect() on the event-loop thread itself, since all
  pycrdt objects there are already created/destroyed on that one
  thread.
@gru3zi
gru3zi requested a review from Che4ter August 11, 2026 11:16
@Che4ter
Che4ter merged commit 9a29e3d into bv-main Aug 11, 2026
10 of 14 checks passed
@Che4ter
Che4ter deleted the promote/tst-milkdown-mfa-collab branch August 11, 2026 13:41
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.

2 participants