Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
7270c8e
fix: jsonl export includes retired (rewound) rows via historian read
Oct 25, 2026
b9f6112
fix(#4665): carry identity + retired_at across archive/restore
Oct 25, 2026
5c8719b
Merge remote-tracking branch 'origin/master' into HEAD
Oct 25, 2026
45fa4c2
merge ci-fixes into 2026-10-25_4665-archive-restore-row-state (#4599 …
Oct 25, 2026
8bbedda
merge ci-fixes into 2026-10-25_4665-archive-restore-row-state (#4599 …
Oct 25, 2026
2f4b0e8
merge ci-fixes into 2026-10-25_4665-archive-restore-row-state (#4599 …
Oct 26, 2026
08fb076
merge ci-fixes into 2026-10-25_export-includeretired (#4599 help-merg…
Oct 26, 2026
1246f56
merge ci-fixes into 2026-10-25_4665-archive-restore-row-state (#4599 …
Oct 26, 2026
c753c3d
merge ci-fixes into 2026-10-25_export-includeretired (#4599 help-merg…
Oct 26, 2026
4a413e8
Merge remote-tracking branch 'origin/ci-fixes' into sweep-wip
Oct 26, 2026
30bcbc8
Merge remote-tracking branch 'origin/ci-fixes' into sweep-wip
Oct 26, 2026
ef2e7ed
fix: self-clear the stale split-brain verdict once the peer stops con…
Oct 25, 2026
cf6994a
fix: show the full session id in the split-brain ack log and web card…
Oct 25, 2026
8f51ea0
fix: stop flagging gracefully-shutdown peers as split-brain writers (…
Oct 26, 2026
4368daf
fix: fail the split-brain evidence check closed when marker evidence …
Oct 26, 2026
fc6a5cf
fix: surface real storage errors after failed BEGIN/COMMIT and auto-r…
Oct 27, 2026
4508a28
test: pin #4949 rollback-masking and breaker recovery regressions
Oct 27, 2026
f292409
Merge remote-tracking branch 'origin/master' into 2026-10-27_storage-…
Oct 27, 2026
8ba5c03
feat(storage): async reload seam + bounded DB pre-warm for network ba…
Oct 27, 2026
72c61b6
test: align storage-mysql-seam suite with the Session API (conflict-l…
Oct 27, 2026
656a3d1
fix: materialize DB-only plugin conversations through the async seam
Oct 27, 2026
64f7da0
test: await the now-async status/reconcile APIs in existing suites (#…
Oct 27, 2026
e91e176
Merge remote-tracking branch 'origin/master' into 2026-10-27_mysql-as…
Oct 28, 2026
97821b6
Merge remote-tracking branch 'origin/master' into 2026-10-27_mysql-as…
Oct 28, 2026
1120610
fix: judge the [instances] multi-instance warning by sessions directo…
Oct 28, 2026
7d02228
docs: single-machine multi-install isolation guidance in README en+zh…
Oct 28, 2026
10a9bc1
fix: route corruption-shaped SQLite throws into the integrity quarant…
Oct 29, 2026
1f230a2
fix: make index.db quarantine robust — name rename failures, never re…
Oct 29, 2026
96826db
fix: report the resolved engine when index.db open fails after resolu…
Oct 29, 2026
238a7e4
fix: sample the tail of dsh child bootstrap output instead of the hea…
Oct 29, 2026
6faac8f
test: regression coverage for corruption-throw quarantine, recovery, …
Oct 29, 2026
9dc0577
fix: repair #5124 CI failures (family gate + two non-hermetic storage…
Oct 29, 2026
950bfaa
fix: degrade to legacy-only when a live foreign bili process owns ind…
Oct 29, 2026
6f44206
Merge origin/master: reconcile #5091 informational reporting with #50…
Oct 29, 2026
87edeaa
test: register issue5046 second-writer suite in LIVE_FILES (#5075)
Oct 29, 2026
41b8697
Merge remote-tracking branch 'origin/ci-fixes' into wt-5075-ci-worker-r1
Oct 29, 2026
234e382
Merge remote-tracking branch 'origin/master' into wt-5075
ranxianglei Oct 29, 2026
9d8b15e
fix(storage): align second-writer degrade log with the supported-topo…
ranxianglei Oct 29, 2026
e4cd055
Merge remote-tracking branch 'origin/master' into pr-4955-sync
Oct 29, 2026
3d9666e
Merge remote-tracking branch 'origin/master' into 2026-10-27_mysql-as…
Oct 29, 2026
72d6b0a
Merge commit '6db5906892ef7cd24f018e5328ad28a47924ec1a' into wt-5124-…
Oct 30, 2026
0a3255a
Merge d-4670: JSONL export includes retired rows, marked by retiredAt…
Oct 30, 2026
9097d0d
Merge d-4672: archive/restore keeps identity + retired_at through rou…
Oct 30, 2026
1ca6b61
Merge d-4955: degraded storage recovers automatically once the source…
Oct 30, 2026
38ea945
Merge d-5123: a corrupted index.db quarantines and recovers instead o…
Oct 30, 2026
19f988a
Merge d-5075: [instances] warning judges by sessions directory, not l…
Oct 30, 2026
6eeb709
Merge d-4630: split-brain alarms clear themselves once provably gone …
Oct 30, 2026
354e42a
Merge d-4987: reload MySQL-backed sessions asynchronously (#4942)
Oct 30, 2026
a0c7ff0
test(mysql-seam): align StubMysqlBackend.getMessages with the retired…
Oct 30, 2026
0a724ce
test(mysql-seam): add #5198-era interface members (sessionViewRows/re…
Oct 30, 2026
42a1968
integrity failure: stop auto-renaming a corrupt index — loud degrade …
Oct 30, 2026
2f16265
Merge remote-tracking branch 'origin/master' into 2026-10-30_classD-d…
Oct 30, 2026
dca899e
merge origin/ci-fixes (9c186cc5): converge inherited 5-guard CI reds …
Oct 30, 2026
f05a405
integrity FAIL verdict: refuse to start instead of legacy-only fallba…
Oct 30, 2026
ffe1465
Merge remote-tracking branch 'origin/2026-10-30_classD-downgrade-cons…
Oct 30, 2026
b76b4f2
Merge remote-tracking branch 'origin/ci-fixes' into wt-5213-ci-worker-r1
Oct 30, 2026
5c7fc51
docs(config): complete the BILI_STORAGE_QUICK_CHECK registration for …
Oct 30, 2026
ad763cf
Merge branch '2026-10-30_classD-downgrade-consolidation' of https://g…
Oct 30, 2026
a054705
docs(config): register the integrity FAIL refuse-to-start semantics i…
Oct 30, 2026
5159ce9
no degradation in ANY case: every unified-sidecar open failure now re…
liangxi Oct 30, 2026
422ea61
merge origin/classD branch (new head since last push)
Oct 30, 2026
b9eb27a
never give up on index writes; integrity FAIL verdicts are alarms onl…
Oct 30, 2026
1766e2d
proven-corrupt index: alarm-only boot — files never moved, copied or …
Oct 30, 2026
b8ba5d7
merge origin/master (#5188 fork/snapshot gates): converge sidecar-reo…
Oct 30, 2026
40bcb2f
fix(tests+docs): three gaps left by the #5188 convergence push
Oct 30, 2026
28454a1
docs(seed): restore the dropped BILI_STORAGE_QUICK_CHECK desc_zh (i18…
Oct 30, 2026
627c258
fix(tests+cli): align three suites with the no-degradation contract
Oct 30, 2026
78a0d4a
merge origin/master (33bc979b): converge #5239 tipseq narrow-read + #…
Oct 31, 2026
7ffda72
merge 627c2580 (no-degradation CLI alignment) into master-convergence
Oct 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ This index is generated from `website/config-reference/*.yaml` — edit the seed
| `BILI_STORAGE_BACKEND` | sqlite\|mysql | sqlite | — | Env tier for storage.backend (#3931): sqlite (default) or mysql (fully wired, #3388 steps 2+3 - saves ingest through the async shadow queue, sync reads ride the JSON envelope, replicas/ext/GC/fork via the async twins (#4911); export too). Wins over the file tier. |
| `BILI_MYSQL_DSN` | string | unset | — | MySQL DSN (mysql://user:pass@host:3306/db) for storage.backend="mysql" (#3388, #3931). Wins over the file key storage.mysql.dsn. Empty = the mysql backend cannot open; the documented error names both knobs. The pool opens lazily; closeQuietlyAsync drains the ingest queue and closes it. |
| `BILI_STORAGE_ARCHIVE_AFTER_DAYS` | integer | 365 | — | GC age sweep window in days (#3463): sessions idle longer than this are archived (one JSON line appended to the single archive log (archive/archive.jsonl) + tombstone row + content/media refs released). 0 disables the age sweep; the manual entry POST /__bili/storage/gc always runs. Config-file key storage.archiveAfterDays is the persistent form. |
| `BILI_STORAGE_QUICK_CHECK` | string | on | — | Storage integrity self-check at open (#4175): every index.db open runs PRAGMA quick_check BEFORE migrations; on failure the shadow layer quarantines the file set (index.db/-wal/-shm renamed to .corrupt-<ts>) and reopens fresh (JSON envelopes remain the recovery source). Set 0 only for boot-time-sensitive huge deployments where the check's cost matters. |
| `BILI_STORAGE_QUICK_CHECK` | string | on | — | Storage integrity self-check at open (#4175): every index.db open runs PRAGMA quick_check BEFORE migrations; if a corruption is PROVEN by THIS BOOT's own scan NOTHING is moved, copied or rebuilt automatically (auto-recovery was withdrawn #4609/#4626-era: with BILI_STORAGE_JSON_WRITER=off the index is the SOLE durable copy, so an automatic move or rebuild cannot guarantee full data recovery) and bili STARTS ANYWAY — serving from the JSON envelopes with a loud, actionable alarm naming the manual fix (back up then move index.db/-wal/-shm aside BY HAND, then start again — a fresh index rebuilds from the JSON envelopes). A successful PASS is cached so later starts skip the rescan; a FAIL verdict is NEVER persisted (2026-10-30 owner policy): runtime/background findings are alarms only (loud logs + runtime-status, the process keeps serving — JSON envelopes remain the recovery source) and every boot decides again from a fresh scan of the CURRENT file set, so a stale FAIL left behind by an older version can never block future boots. There is NO automatic rename/repair AND no legacy-only fallback: continuing legacy-only would fork the JSON envelopes from the dead index forever (never reconcilable). TRANSIENT open failures (no SQLite engine / CANTOPEN / second writer / readonly / backend DSN) make bili REFUSE TO START loudly with an actionable error — no silent legacy fallback for environment problems either (2026-10-30 owner policy); deployments that genuinely want JSON-only serve explicitly (BILI_STORAGE_UNIFIED=legacy or unset). Set 0 only for boot-time-sensitive huge deployments where the check's cost matters. |
| `BILI_SNAPSHOT_SHADOW` | boolean | true (on) | — | Snapshot-render shadow observability (#3756 follow-up): compute the outbound snapshot view on every armed turn WITHOUT using it, classify the diff against the legacy outbound, and surface per-session counters in the web panel + the snapshot-shadow diagnostics topic. 0/false/off disables; 1/true/on enables; otherwise the config-file snapshot.shadow boolean applies (default on). The render lane itself stays rollout-gated (BILI_ROLLOUT_SNAPSHOT_RENDER), so the shadow lane never changes outbound bytes - it is the dry-run evidence that flips the rollout later. |
| `BILI_CONFIG_FILE` | string | unset (XDG config path) | — | Override the config file path (path relocation). |
| `BILI_SESSIONS_DIR` | string | unset (XDG state path) | — | Where session records are stored (path relocation). |
Expand Down Expand Up @@ -2010,7 +2010,7 @@ File keys resolve only when the matching env var is unset. Defaults in parenthes
| `BILI_PERSIST_DEBOUNCE_MS` | Debounce window for persistence writes to disk, in ms (default `500`). |
| `BILI_PERSIST_TAIL_TOKENS` | Token budget for the persisted conversation snapshot (#401). The disk record stores the **folded view** (block summaries in place of compressed ranges) truncated to the newest messages within this budget — never the raw full history. Default `16384`; `0` disables message persistence entirely (block summaries and compressed originals still persist; `bili export` falls back to block-only rendering). Live in-memory sessions are unaffected — `bili export` of a live session is always complete. |
| `BILI_PERSIST_ZSTD` | Set `1`/`true` to enable zstd compression of session files (#1080, owner decision: **default off** — plain JSON maximizes recoverability: jq/grep-debuggable, and immune to the downgrade tail risk). When enabled, every session file that actually shrinks is written as `BILIZSTD1` — a small header (magic + format version + mode byte) over the JSON body, zstd-compressed on Node ≥ 22.15 and stored raw on older runtimes; readers accept both bodies (native zstd, else the bundled WASM fallback), so files stay readable across runtimes and versions. Payloads the framing cannot shrink (tiny sessions) and key-less raw bodies stay bare JSON on disk. Independent of `BILI_ENCRYPTION_KEY` — with both active, the compression choice is recorded in the mode byte inside `BILIENC1`. Existing plain-JSON files are NEVER rewritten at boot (downgrade safety — a mass re-encode would make rolling back to an older bili read its own writes as "corrupt"); they convert organically on their next save. Note: once a session has been saved as `BILIZSTD1`, an OLDER bili (< 0.1.135) cannot read it — this caveat applies only to deployments that opted in; unset or any other value keeps plain JSON. |
| `BILI_STORAGE_UNIFIED` | Tiered storage-migration switch (#2671). Config-file tier: `{"storage":{"unified":"legacy|shadow|full"}}` selects the same modes (strict spellings) and applies whenever the env is unset; `BILI_STORAGE_UNIFIED` (which also accepts `1`/`true` = shadow and `unified` = full) always wins over the file. The mode is resolved once per lane boot — hot edits to the file take effect on the next restart. Set `1`/`true` to enable the **unified SQLite storage dry-run** (Phase 1). Every session write is double-ingested into `<sessions-dir>/index.db` (engine resolution order since #2833: built-in `node:sqlite` when the runtime provides it (Node >= 22.5) -> an already-installed host `better-sqlite3` native module -> a previously self-downloaded cached copy -> a one-time self-download of the pinned `better-sqlite3` prebuilt tarball from the npm registry (~11 MB, sha512-verified, gated by `storage.sqliteAutoDownload`, unsupported platforms never download; engine artifacts are pinned per Node major (Node >= 22: better-sqlite3 v13; Node 20: v12.9.0 plus its ABI-keyed prebuild), and a runtime no pinned artifact serves is refused before any load, with neither the download nor a cached copy registered, since loading such a binding crashes the process (#3341, #3894)) -> none, in which case the sidecar stays legacy-only per the #2825 degrade contract; engine acquisition never depends on the host's install-time build-script approval) and the unified replay is digest-reconciled against the legacy load view — a mismatch logs `[storage] unified shadow DIGEST DRIFT …` and never blocks or alters the write path (legacy files stay the single source of truth). Set `full`/`unified` to enable the **drop-in read path** (Phase 1.5): `loadSync`/`boot`/`loadAll`/`loadStateForSearch` go unified-first — a legacy-file session is organically imported into the index on first open (`[storage] unified import on first open: …`) and later loads are served from `index.db`, with automatic fallback to the legacy file on any unified failure. Legacy files keep being double-written, so **rollback = unset the knob** (env and/or file value). Hard encryption opt-out: while `BILI_ENCRYPTION_KEY` is active the whole sidecar refuses to open (index.db would hold cleartext ingest payloads; a `unified sidecar DISABLED` warning is logged and the store stays legacy-only). Enumeration/GC and the web session list remain file-backed; `removeLegacyFile` now also deletes the index rows and releases CAS refcounts. Digests treat the last-writer `biliVersion` stamp and wall-clock timestamps as environment (`__VER__`/`__TS__`), everything else must match byte-for-byte. The shadow self-disables after 3 consecutive internal failures (in full mode that also flips reads back to legacy-only). **Replica/checkpoint REST (Phase 2)**: on any unified lane (shadow or full), `POST /__bili/replicas {sessionId, label?, pinned?}` freezes the session's persisted state as one content-addressed checkpoint blob + row (the resident session is flushed first), `GET /__bili/replicas[?sessionId=]` lists them, `GET /__bili/replicas/<cpId>` returns detail + byte length, `POST /__bili/replicas/<cpId>/restore {sessionId?}` materializes the checkpoint as a NEW first-class session through the normal dual write path (JSON + unified ingest; `meta.restoredFrom`/`restoredCheckpoint` record provenance), and `DELETE /__bili/replicas/<cpId>` drops it. Pinned checkpoints hold a CAS refcount and survive `deleteSession` GC (the whole point: a copy taken before a destructive experiment outlives it); explicit DELETE still removes even pinned ones. Disk expectation: shadow/full roughly doubles unique-content bytes (every session has a JSON file AND index.db rows sharing a content CAS with no dedup across the two stores) and there is no auto-VACUUM — size the sessions dir accordingly while the migration runs. Legacy-only lanes answer 501 with the knob hint. Routes are loopback-only like the rest of `__bili`. Defaults to `full` when unset (#3818). |
| `BILI_STORAGE_UNIFIED` | Tiered storage-migration switch (#2671). Config-file tier: `{"storage":{"unified":"legacy|shadow|full"}}` selects the same modes (strict spellings) and applies whenever the env is unset; `BILI_STORAGE_UNIFIED` (which also accepts `1`/`true` = shadow and `unified` = full) always wins over the file. The mode is resolved once per lane boot — hot edits to the file take effect on the next restart. Set `1`/`true` to enable the **unified SQLite storage dry-run** (Phase 1). Every session write is double-ingested into `<sessions-dir>/index.db` (engine resolution order since #2833: built-in `node:sqlite` when the runtime provides it (Node >= 22.5) -> an already-installed host `better-sqlite3` native module -> a previously self-downloaded cached copy -> a one-time self-download of the pinned `better-sqlite3` prebuilt tarball from the npm registry (~11 MB, sha512-verified, gated by `storage.sqliteAutoDownload`, unsupported platforms never download; engine artifacts are pinned per Node major (Node >= 22: better-sqlite3 v13; Node 20: v12.9.0 plus its ABI-keyed prebuild), and a runtime no pinned artifact serves is refused before any load, with neither the download nor a cached copy registered, since loading such a binding crashes the process (#3341, #3894)) -> none, in which case the sidecar stays legacy-only per the #2825 degrade contract; engine acquisition never depends on the host's install-time build-script approval) and the unified replay is digest-reconciled against the legacy load view — a mismatch logs `[storage] unified shadow DIGEST DRIFT …` and never blocks or alters the write path (legacy files stay the single source of truth). Set `full`/`unified` to enable the **drop-in read path** (Phase 1.5): `loadSync`/`boot`/`loadAll`/`loadStateForSearch` go unified-first — a legacy-file session is organically imported into the index on first open (`[storage] unified import on first open: …`) and later loads are served from `index.db`, with automatic fallback to the legacy file on any unified failure. Legacy files keep being double-written, so **rollback = unset the knob** (env and/or file value). Hard encryption opt-out: while `BILI_ENCRYPTION_KEY` is active the whole sidecar refuses to open (index.db would hold cleartext ingest payloads; a `unified sidecar DISABLED` warning is logged and the store stays legacy-only). Enumeration/GC and the web session list remain file-backed; `removeLegacyFile` now also deletes the index rows and releases CAS refcounts. Digests treat the last-writer `biliVersion` stamp and wall-clock timestamps as environment (`__VER__`/`__TS__`), everything else must match byte-for-byte. The index write path NEVER gives up: failed ingests are logged (with file + error detail on the first failure) and every subsequent save retries the real write — no self-disable, no cooldown, no fallback mode (2026-10-30 owner policy), while the JSON envelope keeps serving reads meanwhile. **Replica/checkpoint REST (Phase 2)**: on any unified lane (shadow or full), `POST /__bili/replicas {sessionId, label?, pinned?}` freezes the session's persisted state as one content-addressed checkpoint blob + row (the resident session is flushed first), `GET /__bili/replicas[?sessionId=]` lists them, `GET /__bili/replicas/<cpId>` returns detail + byte length, `POST /__bili/replicas/<cpId>/restore {sessionId?}` materializes the checkpoint as a NEW first-class session through the normal dual write path (JSON + unified ingest; `meta.restoredFrom`/`restoredCheckpoint` record provenance), and `DELETE /__bili/replicas/<cpId>` drops it. Pinned checkpoints hold a CAS refcount and survive `deleteSession` GC (the whole point: a copy taken before a destructive experiment outlives it); explicit DELETE still removes even pinned ones. Disk expectation: shadow/full roughly doubles unique-content bytes (every session has a JSON file AND index.db rows sharing a content CAS with no dedup across the two stores) and there is no auto-VACUUM — size the sessions dir accordingly while the migration runs. Legacy-only lanes answer 501 with the knob hint. Routes are loopback-only like the rest of `__bili`. Defaults to `full` when unset (#3818). |
| `BILI_PERSIST_EPERM_ALERT_THRESHOLD` | N consecutive persist write failures (`EPERM`/`EBUSY`/`EACCES`) on one session before the one-time "add this dir to antivirus exclusions" alert fires (default `5`). Windows only. See [Windows: exclude the sessions dir](#windows-exclude-the-sessions-dir-from-antivirus-362). |
| `BILI_PERSIST_EPERM_ALERT_REPEAT_MS` | Re-alert window for the persist EPERM alert, in ms. `0` (default) = alert once then stay silent; `>0` = re-alert at most every that many ms while the failures continue. |
| `BILI_TUNNEL_ALLOWED_HOSTS` | `/bili/<absolute-url>` tunnel admission for **remote clients** (#409): comma-separated `host` or `host:port` entries that unlock loopback/private destinations (e.g. a LAN relay or the machine's own sglang) for non-loopback clients. The proxy itself and link-local/metadata addresses are always denied; local (loopback) clients always pass. |
Expand Down
2 changes: 1 addition & 1 deletion CONFIGURATION.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@
| `BILI_STORAGE_BACKEND` | sqlite\|mysql | sqlite | — | storage.backend 的环境变量层(#3931):sqlite(默认)或 mysql(已接线,#3388 第二+三步 —— 保存经异步影子队列入 mysql,同步读走 JSON 信封,replicas/ext/GC/fork 走异步孪生(#4911);export 同样支持)。优先于文件层。 |
| `BILI_MYSQL_DSN` | string | unset | — | storage.backend="mysql" 的 MySQL DSN(mysql://user:pass@host:3306/db)(#3388,#3931)。优先于文件键 storage.mysql.dsn。留空 = mysql 后端无法打开;文档化错误会列出两个旋钮。连接池惰性打开;closeQuietlyAsync 排空入队事务并关闭。 |
| `BILI_STORAGE_ARCHIVE_AFTER_DAYS` | integer | 365 | — | GC 年龄清扫窗口(天,#3463):闲置超过该时长的会话被归档(单文件归档日志 archive/archive.jsonl 追加一行 + 墓碑行保留标签 + 释放内容/媒体引用)。0 关闭年龄清扫;手动入口 POST /__bili/storage/gc 始终可用。配置文件键 storage.archiveAfterDays 为持久形式。 |
| `BILI_STORAGE_QUICK_CHECK` | string | on | — | 存储开库自检(#4175):每次 index.db 打开在迁移之前先跑 PRAGMA quick_check;失败时影子层把文件组(index.db/-wal/-shm 改名为 .corrupt-<ts>)隔离并重建新库(JSON 信封仍是恢复源)。仅当超大库的启动时间敏感时才设 0 跳过。 |
| `BILI_STORAGE_QUICK_CHECK` | string | on | — | 存储开库自检(#4175):每次 index.db 打开在迁移之前先跑 PRAGMA quick_check;一旦由本次启动自己的扫描证实损坏,文件原样保留在原地——不做任何自动搬移、复制或重建(自动恢复已撤回:BILI_STORAGE_JSON_WRITER=off 时索引是唯一的持久副本,自动操作无法保证完整数据恢复)——bili 照常启动,改用 JSON 信封服务,并给出响亮、可操作的告警指明手工修复步骤(备份后把 index.db/-wal/-shm 手工移开再启动——全新索引会从 JSON 信封重建)。PASS 判定会被缓存,之后的启动跳过重扫;FAIL 判定永不持久化(2026-10-30 owner 策略):运行期/后台发现只是告警(响亮日志 + runtime-status,进程继续服务——JSON 信封始终是恢复来源),每次启动都对当前文件集做新鲜扫描再判定,旧版本遗留的陈旧 FAIL 永远不能阻断后续启动。既没有自动改名/修复,也没有 legacy-only 兜底(继续以 legacy-only 运行会让 JSON 信封与死掉的索引永久分叉、再无对账可能)。瞬态打开失败(无引擎 / CANTOPEN / 第二写者 / 只读 / 后端 DSN)仍响亮地拒绝启动并给出可操作的错误——环境类问题同样不允许静默走 legacy 兜底;真正需要纯 JSON 服务的部署请显式设置 BILI_STORAGE_UNIFIED=legacy(或留空)。仅当超大部署对启动耗时敏感、检测成本构成问题时才设 0 跳过。 |
| `BILI_SNAPSHOT_SHADOW` | boolean | true (on) | — | 快照渲染影子对账(#3756 后续):每轮已折叠会话都用引擎现算一次出站快照视图但不使用,与 legacy 实际出站比对分类,逐会话计数进 web 面板与 snapshot-shadow 诊断主题。0/false/off 关闭;1/true/on 开启;否则读配置文件 snapshot.shadow 布尔(默认开)。渲染车道本身仍受灰度开关控制(BILI_ROLLOUT_SNAPSHOT_RENDER),影子车道绝不改变出站字节 —— 它是后续翻转灰度的干跑证据。 |
| `BILI_CONFIG_FILE` | string | unset (XDG config path) | — | 覆盖配置文件路径(路径重定位)。 |
| `BILI_SESSIONS_DIR` | string | unset (XDG state path) | — | 会话记录的存放目录(路径重定位)。 |
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,22 @@ Consequences for anyone tempted to put several instances behind one Service:
conversation load-balanced across proxies*. Long conversations are
single-instance by design.

**Single machine, multiple installs (#5046).** The topology above covers
deployment replicas; one desktop has a sibling hazard: two installs on ONE
machine (e.g. two DSH profiles — desktop + bot, or a global npm copy beside a
host-tree copy) each start their own proxy and, unless isolated, both resolve
the SAME default XDG sessions directory and the same config file. With
`storage.unified` on (shadow/full), two processes then double-write one
`index.db`: the log shows `[instances] another bili instance is running …`
and, for overlapping lanes, `[storage] … two processes on one index.db
corrupt it`; SQLite write-lock contention (`SQLITE_BUSY` / `database is
locked`) can fail whole model turns, and sustained double-writing risks
corruption. The fix is isolation: give each install its own
`BILI_SESSIONS_DIR` (plus `BILI_CONFIG_FILE` when the config should differ
too) through that install's launcher/profile environment — no global env
changes needed. Once isolated, the installs coexist safely and the
`[instances]` notice drops to info level (different dirs ⇒ no shared state).

Naming note: `POST /__bili/replicas` creates a session **checkpoint fork**
(from stored checkpoints), not a pod replica — read it as `/__bili/checkpoints`.

Expand Down
Loading
Loading