# BF33 / TX7.1 — Production Security & Collaboration Lifecycle Hardening

Date: 2026-08-22

## Objective

Harden the production boundaries around BF32/TX7 without changing its core collaborative editor model. Live manuscript editing remains Source-only and authoritative through `CodeMirror ↔ y-codemirror.next ↔ Y.Text ↔ y-websocket`; Visual collaboration, custom cursor mapping, authorship CRDTs, Follow/Open and live project-tree CRDT operations remain absent.

## Implemented state

### Administrator provisioning and authentication

- Fresh SaaS startup no longer creates `admin@texise.com` with a known password.
- The operator provisions or resets the reserved administrator explicitly with `sudo texise-saas admin-create`; password entry requires an interactive TTY, explicitly disables terminal echo, and is not passed via command argv/environment files.
- On upgrade, only the exact historical `admin/admin` credential is revoked automatically. Its active sessions are invalidated. A secure changed administrator password is left untouched.
- Administrator login throttling is persisted in MySQL for IP and normalized-account buckets, complementing the fast in-process limiter.
- Expired sessions and old throttle buckets are cleaned automatically.

### Collaboration relay

- Global room, socket, per-IP connection, and per-IP new-room creation ceilings are enforced.
- Y.Doc state is bounded; expensive full-document size measurement is coalesced to a 500 ms interval and an oversized room is evicted.
- A binary file channel can attach only when the same diagnostic client ID has an active Yjs collaboration connection; it cannot create a room independently.
- Duplicate collaboration/file client IDs are rejected.
- Binary requests are forwarded to one source peer; binary chunks are delivered only to the requesting peer.
- Missing preferred sources fall back cleanly without retaining the stale preferred ID.
- Per-peer/per-room request limits, per-file maximum, per-minute byte budget and stale-request TTL prevent abandoned/flooded transfers from consuming resources indefinitely.

### Collaboration session lifecycle

- Reconnect descriptors store Host/Guest role plus Host session ID.
- Host refresh/reopen reconnects with the same Host identity rather than returning as Guest.
- Stop writes the TX7 stopped-session tombstone, persists it, waits for the WebSocket outbound queue to drain, then disconnects.
- Explicit Stop/Leave forgets local reconnect credentials and clears that room's collaboration persistence. Ordinary refresh intentionally retains them.
- Retained collaboration IndexedDB databases are registered, capped and age-pruned; BF1 legacy migration no longer runs from every session constructor.

### Project snapshot and local recovery

- Host readiness seals the project snapshot and records a manifest. Normal TX7.1 APIs refuse initial file/directory/binary/name/root mutations once sealed; existing supported Y.Text files remain editable.
- The recovery directory `.texise-local-recovery/` is excluded from collaboration snapshots.
- Same-room materialisation compares the local project with the previous incoming snapshot hash baseline.
- Extra local files/directories and same-path files changed locally since that baseline are preserved under `.texise-local-recovery/<timestamp>/...` before the current collaborative snapshot overwrites/deletes them.
- Snapshot baselines are age-pruned with collaboration persistence lifecycle data.

### Canonical Source and performance

- `ProjectCollaborationService.getCanonicalSourceText()` reads active Y.Text during collaboration and LatexSource otherwise.
- Source editor actions, AI, citation insertion, TikZ insertion and Paper Memory resolve collaboration-sensitive text through the canonical accessor/helper.
- Y.Text remains the only live collaboration text authority; LatexSource/project storage remain projections.
- Final session teardown updates both persisted project files and the in-memory LatexSource projection.
- Semantic project refresh after Y.Text changes is debounced instead of running once per animation-frame mutation.

### Security/dependency maintenance

- Direct `ws` dependencies: 8.21.3.
- Nodemailer: 9.0.5.
- Production CSP no longer permits arbitrary `wss:` destinations; it permits the canonical TeXise WebSocket origin while preserving required HTTPS provider access.
- Current documentation describes the Project link as a bearer trusted-editor capability. Host state is not represented as cryptographic authorization.


### Dependency-backed installer follow-ups

- Production `tsgo`/mobile-entry typecheck reached clean completion after the BF33 typecheck and Paper Memory nominal-path hotfixes.
- The subsequent Biome pass found one style-only E2E issue (`lint/style/noUnusedTemplateLiteral`) in the `/join/<room>` URL assertion; the regex now uses a plain string literal with unchanged semantics.

## Verification completed in the extracted source environment

- BF33 source guard: PASS (and invokes the complete BF32/TX7 guard).
- Frozen lock sync: 69 workspace importers and 1171 reachable dependency snapshots PASS; every reachable package/snapshot closure entry is validated before pnpm install.
- Workspace dependency version consistency: 68 packages PASS.
- Public barrel validation: 67 entries across 68 packages PASS.
- SaaS administrator/authentication Node tests: 11/11 PASS.
- R14.28.14 focused regression: 22/22 PASS.
- Runtime hardening: 101/101 PASS.
- Static release audit: 350/350 PASS.
- SaaS static audit: 43/43 PASS.
- Modified TS/TSX syntax transpile: 11/11 files PASS; modified Node scripts PASS `node --check`; modified shell scripts PASS `bash -n`; modified package JSON parses; `git diff --check` PASS. Archive integrity/top-level layout/checksum are verified after ZIP creation.

## Authored but not executable in this source-only environment

The collaboration browser suite contains BF33 contracts for exact named-cursor placement, Host refresh/reclaim, same-room local divergence recovery, Stop behavior, reconnect convergence, compile while collaborating, same-link reuse and the formal 10,000-edit soak. The relay spec covers active-Yjs-only file channels and targeted source/requester routing.

The extracted archive has no installed project `node_modules`/browser runtime, so the following are **not claimed as executed here** and remain mandatory staging/production release gates:

- dependency-backed full TypeScript/tsgo check;
- Biome;
- Vitest suites requiring workspace dependencies, including the signaling spec;
- Vite production build;
- Playwright cross-browser collaboration tests;
- actual formal 10,000-edit/30-minute soak;
- live deployed `/collab-live`/proxy certification.

## Operator action after upgrade

If BF33 startup/preflight reports that the historical default administrator credential was revoked, run:

```bash
sudo texise-saas admin-create
```

and enter a new strong administrator password interactively. An existing administrator that had already changed away from the historical default requires no reset.

## Release decision

BF33/TX7.1 is source-complete when the final dependency-free syntax/static/archive checks pass. Production activation still requires the external dependency-backed/browser/live-relay gates listed above.

## Installer lockfile integrity hotfix

The first BF33 archive exposed a latent duplicate-key block in `pnpm-lock.yaml`: importer specifier checks passed, but pnpm correctly rejected the YAML at install time. The repaired lock removes the 12 later duplicate snapshot entries (all were byte/semantically identical to their earlier definitions). `verify-pnpm-lock-sync.mjs` now validates duplicate direct mapping keys before checking importer specifiers, and `repair-buildfix33-source.mjs` executes that validator, so this class of lock corruption fails before `pnpm install`.

A second installer run exposed a separate incomplete frozen-lock closure for BF32's QR type dependency: the `packages/core/app` importer referenced `@types/qrcode@1.5.6`, but `packages:`/`snapshots:` lacked its resolved node. BF33 now includes the upstream integrity record and snapshot dependency on `@types/node 26.2.0`. The lock validator now traverses all non-workspace importer resolutions and their reachable snapshot dependency graph (1171 reachable nodes in this release) and fails on any missing `packages:` or `snapshots:` entry. A negative regression removes the QR type package record and confirms the preflight rejects it before pnpm runs.
### Paper Memory nominal-path follow-up

The first production rerun reduced `tsgo` from 33 errors to four, all in Paper Memory. `SourceAnchor.filePath` and normalized bibliography targets are represented as strings at those call sites, while `canonicalSourceState()` intentionally accepts only the branded `ProjectFilePath` type. BF33 now converts all four affected values through `projectFilePath(...)` before canonical Source access instead of weakening the helper signature. The BF33 guard certifies those exact conversions.

## Dependency-backed typecheck hotfix

After the repaired frozen lock allowed the production installer to complete dependency installation, the real `tsgo` build exposed 33 strict typing errors across eight implementation areas. The corrected BF33 source fixes every reported error without changing the TX7.1 architecture: duplicate TikZ state is removed and canonical text is used consistently; React 19 refs are initialized explicitly; six file-structure command declarations now expose the `projectCollaboration` service already used by their handlers; AI snippet insertion restores its resolved `WsPath`; Paper Memory imports its `ProjectFilePath` type; obsolete Review commit state is removed; the Yjs state vector is copied into an ArrayBuffer-backed `Uint8Array` for WebCrypto; and the hardened signaling relay now has explicit check-JS types for HTTP requests, transfer data, admission maps and the supported `ws` `maxFragments` option.

The BF33 source guard now includes regression assertions for those fixes. Dependency-free verification remains green on the corrected source and the frozen lock remains unchanged at 69 importers / 1581 package records / 1583 snapshots / 1171 reachable snapshots. The next production installer rerun is the authoritative dependency-backed `tsgo`/Biome/build verification for this hotfix.


- **BF33 live collaboration health-check hotfix:** updated `scripts/check-collaboration-live.mjs` to use the same active Yjs client IDs for the hardened file channel, request a specific source peer, and send/verify a valid TXB1 binary chunk. The previous health probe still used BF32 broadcast/raw-byte semantics and therefore timed out against the correctly hardened BF33 relay.
