SAFE_ROUTES has 19 entries (backends.js), not 18. reference.md and
reference.zh-CN.md both said '18 entries'; both now say 19.
reference §4 said the /api/enabled and /api/admin/session calls are
'admin-session Bearer-auth'd'. The code says otherwise: config_authorized
(notify.rs:423) accepts the shared worker token OR a valid admin session,
and create_admin_session (notify.rs:1187) is gated by worker_authorized
against the admin credential CONFIG_ENCRYPTION_KEY — it is what mints the
session, so it cannot be session-auth'd. Both languages now state the two
distinct credentials and why adding /api/enabled opens no new credential
path.
deployment §10.8 said every request-bearing entry point, listing /healthz
and /api/*, calls refresh_business_config. The actual 10 call sites cover
8 distinct paths (/ready, /worker, /reconcile, /push/jmap, /webhook/tg,
/api/config, /api/business-config, /api/telegram/register-webhook). GET
/healthz is a static 200 "ok" and never refreshes; /api/status,
/api/enabled, /api/push/register, /api/push/disable and the
/api/admin/session pair do not either. Both languages now list the real
set and name the exclusions.
design.md §5.7 listed the search trigger prefixes as English
'search'/'find'/'retrieval'; worker.rs:689 accepts the Chinese prefixes
搜索/查找/检索 and the help text states English aliases are not supported.
The two user-facing strings were also mistranslated: the empty-query
guidance prompt is 请提供搜索关键词,例如:/search 发票 (worker.rs:623)
and the no-match reply is 没有找到匹配「<query>」的邮件。
(worker.rs:792). The degraded-rendering marker is likewise a Chinese
string. SEARCH_SUBJECT_MAX=120 / SEARCH_PREVIEW_MAX=160 /
SEARCH_LIMIT=10 verified unchanged.
design.md §5.6/§5.7 were machine-translated from Chinese back into
English and were broken: 'the first 300 if it fails. characters', a
stray closing ** mid-sentence, '(none) deny_unknown_fields)' with a
mismatched paren, and 5 zero-width-space artefacts. charter.md:168 had a
broken '**Tables, lists, or values may not be copied across documents. **'
and opengaps.md had 4. All ZWSPs removed from docs/.
deployment.zh-CN.md §10.5/§10.6 were thin mistranslations: 'Worker 域'
for 'Worker URL', the debug-token requirement dropped entirely, and a
mangled '两者都使用用于验证部署的可用和版本'. Rewritten against the EN.
Also replaced a worker.rs:227 line anchor with the MetadataWorker doc
comment; that drops the repo's line-refs from 72 to 71, so new text no
longer adds to the drift surface.
No code changes; GATE-DOCS 6/6 (71 anchors, 455 section refs, 727 path
refs).
The zh translation was generated by a machine translator that treated
backtick-delimited literals as prose. It therefore translated Redis
keys, function names, trigger words and status labels, which made the
documentation technically wrong rather than merely stylistically bad:
config:business -> 配置:业务
config:outbound -> 配置:出站
config:admin_session -> 配置:admin_session
consent:ai:{chat_id} -> 同意:ai:{chat_id}
delivery:inflight/committed
-> 交付:飞行中 / 交付:已提交
dedup:jmap:{account}:{email}
-> dedup:jmap:{帐户}:{电子邮件}
ratelimit:push-verify:* -> 速率限制:推送验证:*
push:subscription:* -> 推送:订阅:*
state:jmap:since -> 状态:jmap:自
lock:reconcile -> 锁定:协调
stalwart:*:dlq -> 坚定:*:dlq
set/get_business_config, set_enabled, is_enabled,
remember_push_subscription_id -> Chinese prose
Same damage class, three more forms:
- a typo: delivery:committed -> delivery:comfilled
- a case change that breaks a machine-readable code:
429 push_verify_rate_limited -> 429 Push_verify_rate_limited
- translated trigger words: 直到我取消 / 撤销授权 / 停止摘要 instead of
直到我撤销 / 撤销授权 / 停止摘要
Also repaired:
- six table headers 关键|TTL|作家|读者 -> 键|TTL|写入方|读取方
- a stray TTL number duplicated into the label column (3600 1小时)
- the AI-consent label row (直到截至 -> 直到撤销(最长365天))
- a dropped aside, 因此递增, which silently removed the reason why
increment, DLQ append and source ACK are atomic
- the 搜索/查找/检索 prefix list, where 检索 had been normalised to 搜索
Section 4 was rewritten rather than patched: the paragraphs carried a
dangling untranslated 'all:' and 'proxy', a dropped closing '**' on
**is forwarded**, 'two calls' rendered as 两个电话, and word order that
reversed the meaning of 'no second ingress is needed'.
Verification: headings and table row counts already matched the English
file exactly (6 section pairs, no structural drift), so this is a
content repair, not a re-sync.
No code changes; GATE-DOCS 6/6 (72 anchors, 455 section refs, 727 path
refs).
Reframe the Cloudflare Worker gateway across the entry points where a
reader forms an impression of it, in both languages:
- README §2 / §5 (en+zh): the gateway is optional and exists because
Cloudflare's Load Balancer product is not on the free plan. Drop the
comparison row that listed 'only the 19 safelisted paths' against
'every route' as if the safelist were a benefit; replace it with
backend reachability, which is identical either way, and move the
route-count fact to its own neutral row. Add one sentence to §5 that
the gateway is outside the secret boundary.
- deployment.md §10 (en+zh): purpose is load balancing, not protection;
no intent to hide or harden the backends, no access control at the
edge, the allowlist is not a firewall.
- cloudflare-worker README (en+zh): new 'why it exists' / 'why it is
not a security feature' block, plus a 'not a security layer' bullet.
Drop 'access-hiding capability' from the non-capabilities list, since
claiming invisibility as something we refuse to provide implies it is
the interesting axis.
- reference.md §4 / §9.2 (en+zh) and deployment.md zh §2: stop saying
bootstrap and /debug/* are 'not exposed' / 'kept off the public path'.
They are simply not in the route set, which is why a request to the
Worker never reaches them — reachable at the backend's own address
either way.
- charter ARCH-LB-WORKER (en+zh): 'only external entrance' -> 'only
*registered* entrance', which is what C-LB-SINGLE-REG-URL actually
says, and backends stay directly reachable.
SAF-LB-PASSTHRU already records the security-side invariant (no auth
or body rewriting, backend may be directly connected to the public
network), so no new stable ID is needed. No code changes; GATE-DOCS
6/6.
The retire-on-change commit inserted ~500 lines into notify.rs and ~110
into state.rs, shifting every anchor at or after the insertion point.
The ANCHOR audit only proves the referenced line exists and is
non-empty, so all of these passed while pointing at the wrong code:
deployment.md/.zh-CN register_telegram_webhook 1619 -> 1623
register_push 1464 -> 1468 (EN)
register_telegram_webhook 1439 -> 1623 (zh)
register_push 1315 -> 1468 (zh)
retired.md/.zh-CN registration_lock fmt 1328-1330 -> 1481-1483
register_push 1315 -> 1468
state.rs read_batch 491 -> 506
state.rs query_async 531 -> 546
state.rs XGROUP CREATE 499-507 -> 514-522
opengaps.md/.zh-CN get_reconcile_state 777 -> 792
set_reconcile_state 785 -> 800
acquire_lock 1076 -> 1130
Every target was verified against the source before editing: two of the
pre-existing anchors pointed inside a different function than the one
they named, and the EN deployment.md had both register anchors off while
the zh twin still carried the original pre-move numbers.
Also documents the new push:registration:current key in the Redis key
table (writer, readers, TTL, and why its literal suffix cannot collide
with the hex session_digest suffix) and carries the zh twin the EN
version already got. Mirror the retire-on-change contract in
deployment.zh-CN.md - the zh twin has its own callback section, which
the EN update skipped.
Four spots told the operator to read the SPA footer to confirm a
deploy landed. The footer no longer renders a version, so restating
that would send an operator looking at a control that no longer
exists. Point them at /api/status instead; the version field itself is
unchanged.
The previous commit inserted 500+ lines into notify.rs, moving
register_push and register_telegram_webhook. Reanchor the two
deployment.md line refs; the other seven refs in that file are before
the insertion point and are unchanged.
Document the new retire-on-change contract in reference.md.