MessageWeave/docs/charter.md

31 KiB
Raw Blame History

MessageWeave — Project Charter

中文版本 / Chinese version → charter.zh-CN.md

This document is an exclusive specification for the **project: project goals, technology selection, safety invariants, implementation stages, prohibited matters, Test acceptance, document boundaries and stable ID registry.

For general, language-independent code writing and environment construction specifications, see ../AGENTS.md; The single authoritative source of verifiable facts (routing / Redis keys and TTL / configuration items / error codes / outbound constants) see reference.md; for gaps and blocking, see opengaps.md.

When there is a conflict between the three: **The verifiable facts shall prevail in reference.md, the project constraints shall prevail in this document, and the quality rules shall prevail in ../AGENTS.md. **

Code Baseline fc686ab (src/ line number anchor)|The document is based on the current main, and the anchor point must be reviewed along with the code baseline.


1. Project goals

A JMAP email notification service deployed on Docker + externally hosted Redis:

  • Read-only pull email changes through JMAP to determine whether notification is needed.
  • Optional LLM analysis (AI switch + user-level external authorization), the analysis results are not stored or entered into the message.
  • Push metadata only notifications to whitelisted chat via Telegram.
  • Users interact with /help through Telegram commands (/summary, /search).
  • Configure business parameters through browser SPA; switch business processing on and off through protected interfaces.

Not done: Multi-tenancy, account system, clients other than Web UI, message persistence, and two-way email synchronization.

2. Technology selection (locked)

Layer Selection Version
Language Rust edition 2021
Web framework axum 0.8
asynchronous tokio 1
State Storage Redis redis 0.27
JMAP Client jmap-client 0.4.2 (default-features = false + features ["async","rustls"])
Telegram Self-built lightweight client (src/channel.rs), not introducing bot framework —
HTTPS client reqwest 0.13
key type secrecy 0.10
encryption ring 0.17
Log tracing + tracing-subscriber 0.1 / 0.3
Scheduling External cron → POST /reconcile —
Front-end Native HTML + single-file JS + single-file CSS, No build tools, no frameworks —
Gateway Cloudflare Worker (Pure JS, zero dependencies) —

3. Security boundary (cannot be violated)

23, numbered SAF-* / REQ-* / C-*, all registered in the §8 register.

  1. JMAP read-only: The Send class interface is not allowed, and any modification of the mailbox content is prohibited.
  2. No outbound sending: src/channel.rs is the only Telegram outbound exit; direct sending of messages is prohibited at the domain layer.
  3. Long connections are prohibited: WebSocket/SSE/long polling must not be introduced; all communications are short request-response.
  4. Stateless Recovery: Do not use local files or memory states for recovery; only rely on replayable records in Redis after a crash.
  5. No database: Relational or document databases are not allowed.
  6. No local writing: The process does not write any files except the stdout log.
  7. The log only goes to stdout: no log file is written, and no external log agent is connected.
  8. State only in Redis: All mutable states only exist in externally managed Redis.
  9. Keys only come from environment variables: Process configuration is not read from Redis.
  10. Keys are not echoed: Any logs, error responses, and Debug output must not contain the key body.
  11. Key constant time comparison: Admin credentials and admin session digest must be compared in constant time.
  12. Notifications only contain metadata: Telegram notifications only contain sender/subject/time (+ number of attachments), and the text never enters the notification.
  13. AI results do not enter messages: LLM output is only used for decision-making and does not enter Redis or Telegram messages.
  14. AI requires user-level external authorization: Only chats that have been explicitly authorized and have not expired will trigger LLM; authorization is an external behavior and the process does not authorize on behalf of it.
  15. Business Whitelist: Outbound Telegram chat must hit CHAT_ALLOWLIST.
  16. The diagnostic interface is closed by default: /debug/* requires DEBUG_ENABLED (or --debug) + DEBUG_TOKEN to be satisfied at the same time.
  17. The diagnostic interface does not enter the gateway: /debug/* is not in Worker SAFE_ROUTES and can only be directly connected to the backend origin.
  18. The gateway only transparently transmits and does not make decisions: Worker does not parse the request body, does not verify the business logic, and does not issue Retry-After.
  19. No retry for AI results: Failure of LLM analysis will be regarded as "no notification", no retry, and no downgrade to no AI notification.
  20. Stateless process: Two container instances are safe to run concurrently; no local writes.
  21. No long polling scheduling: Scheduling is triggered by external cron POST /reconcile.
  22. AI analysis is not persistent: The analysis results and intermediate states other than the authorized state are not recorded.
  23. JMAP capability dynamic discovery: Hard-coded JMAP capability list is prohibited and must be read at runtime.

4. Implementation phase (historical sequence, used to explain code structure)

Stage Content Stable ID
0 Configuration/state storage interface abstraction, HTTP skeleton ARCH-DEPS-STAGE0
1 JMAP read-only adapter ARCH-DEPS-STAGE1, MOD-JMAP-CLIENT
2 Telegram channel self-built client (without introducing bot framework) ARCH-DEPS-STAGE4, MOD-TELEGRAM-NOTIFY
3 AI optional access REQ-AI-EXTERNAL-CONSENT, REQ-AI-FUSE
4 Redis implementation ARCH-DEPS-STAGE4
5 Production (debug surface, gateway, reconciliation) MOD-DEBUG, SAF-DEBUG-GATE

**Phase numbers are historical order, not current status statements. ** The current status is subject to reference.md and code.

5. Prohibited matters

  1. **Dependencies not registered in §2 of this document may not be introduced. ** New dependencies must be registered in this document first and the reasons given.
  2. **Do not delete tests, skip verification, relax inspection levels, or delete access controls in order to pass the access control. **
  3. **Do not reference non-existent dependencies, functions, types, Redis keys or line numbers in documentation or comments. **
  4. You are not allowed to write "plan to implement X" comments in the code; placeholder implementations must be explicitly marked as placeholders.
  5. No build tools or frameworks may be introduced in SPA/Gateway; web/ maintains single-file JS + single-file CSS.
  6. The SPA must not be allowed to persist any credentials (no localStorage / sessionStorage / Cookie assignments).
  7. Unverified code shall not be submitted; the access control results must come from the actual execution of this round.
  8. **Do not claim to have passed the real environment verification unless it is really connected this round. **
  9. Front-end resources must not be introduced outside web/ and cloudflare-worker/.
  10. **Business configuration must not be allowed to silently fall back to environment variables. **

6. Test acceptance and access control

6.1 Change verification closed loop

Any changes involving runtime behavior or verifiable facts are done within the same change: Implementation → Testing → Documentation.

You must not change the code without changing the documentation, and you must not change the documentation without changing the code.

6.2 GATE-P0 (code access control)

Executed in the Debian minimum release container, the container must explicitly export PATH=/usr/local/cargo/bin:$PATH:

cd /home/okabe/Repo/messageweave && docker run --rm --user 1000:1000 \
  -e HOME=/tmp -e RUSTUP_HOME=/app/.gate-cache/rustup -e CARGO_HOME=/app/.gate-cache \
  -v "$PWD":/app -w /app rust:1-slim-bookworm \
  bash -lc 'export PATH=/usr/local/cargo/bin:$PATH; cargo fmt --all -- --check && cargo check --locked && cargo clippy --locked --all-targets -- -D warnings && cargo test --locked 2>&1 | tail -12'

Current baseline: 93 passed / 0 failed / 4 ignored (Add 4 new cursor parsing and walk paging and retest after single test; ignored: real_server_tests::session_list_and_read_smoke and debug::tests::debug_config_reports_timezone_of_business_configured_app require external real JMAP Server credentials, real_redis_ttl_tests::ttl_claim_dedup_sets_the_exactly_requested_expiry and real_redis_ttl_tests::ttl_consent_and_retry_landing_on_real_redis require REDIS_TEST_URL).

Note: The bash -lc script of docker run must be wrapped in single quotes. Using double quotes will first expand $PWD / $PATH on the host, and cargo will not be found in the container.

6.3 GATE-DOCS (Document Access Control)

cd /home/okabe/Repo/messageweave && bash scripts/docs_check/run_all.sh

Pass all 6 validators and exit 0. Read only, never write files.

Added check_file_size: Source files with more than 500 lines under src/, web/, cloudflare-worker/src/ must have SPLIT-EVAL: tag (AGENTS.md §2.1). It proves that the mark exists, not that the written reasons for the split are tenable.

Added audit_paths: The path tokens in backticks in docs/*.md and AGENTS.md must actually exist. The reason for its existence is that the document once appeared with a path that was never created, and the fictitious path read exactly like the real path, and the reader could not tell the difference. "We decided not to do it" and "This is wrongly written". docs/retired.md is also within the scope of the check - the path in the abandonment registration must also be true, The ungrounded form is only described in words. There are only three types of exemptions, all visible in the script: Owner prefix pointing to external repository (stalwartlabs/...), git history to document deleted files, and a justified whitelist of well-known filenames. It does not check the prose outside the backticks, and the bare file name is only parsed according to the same name verification.

0 error only proves "reachability": the line number exists, the reference can be resolved, the number of table columns is consistent, and the mark is in place. **It does not prove semantically correct. ** This distinction must be stated when reporting verification results.

6.4 Front-end testing

cd /home/okabe/Repo/messageweave/web && node --test *.test.mjs
cd /home/okabe/Repo/messageweave/cloudflare-worker && node --test test/*.test.js

6.5 Invariants must have assertions

Each safety invariant in §3 must have a test or code-level assertion that proves it holds. Rather than just writing it in the document. Pure refactoring must not change the total number of tests.

6.6 External dependency testing

The business credentials of Stalwart and Telegram have been passed through real machine joint debugging - outbound, query and inbound callback directions All have been verified (the registration, callback, and verification of setWebhook and PushSubscription are all passed), hosted Redis Connected. Real environment integration tests must be marked with #[ignore] and will be silently skipped if credentials are missing (credentials will not be leaked and failure will not be judged).

**Environmental blocking items: None. ** The last two items have also been verified: the TTL value was measured on a real Upstash instance (SET … NX EX / SET … EX 3600 / Lua EXPIRE 86400 three atomic write points, running for 6 consecutive rounds, all green), newState of Email/changes was triggered in a real Stalwart environment (see docs/design.md §10.5). opengaps.md The "blocking" area is currently empty.

7. Document boundaries

Answer only one question per document; **Tables, lists, or values ​​may not be copied across documents. **

Documentation What to answer Scope of authority
../AGENTS.md Universal, language-independent code writing and environment building specifications Quality rules
README.md / README.zh-CN.md User-oriented: what is it, how to run it, how to configure it User-visible facts (information must be equivalent in Chinese and English)
docs/design.md Why it is designed this way: data flow, module boundaries, state machines, error handling Architectural intent
docs/deployment.md How to deploy: Dockerfile, secrets, gateway, multiple instances, cron Deployment and operation and maintenance
docs/reference.md The single authoritative source of verifiable facts: routing, Redis keys and TTLs, configuration items, error codes, outbound constants verifiable facts
docs/opengaps.md Gaps, Blockages, Next Stage Goals Open Items
docs/retired.md Records and alternative links to abandoned or renamed plans Historical decisions
docs/charter.md This document: project constraints, technology selection, implementation stage, security invariants, stable ID registry Project constraints
web/ (no documentation, 4 files) Manage SPA sources: static configuration pages and front-end logic, covered by web/config.test.mjs Front-end behavior (authoritative facts documented in docs/design.md and docs/reference.md)
cloudflare-worker/README.md / cloudflare-worker/README.zh-CN.md Configuration and semantics of the gateway itself Gateway

Cross-document citation rules:

  • Disable the use of §x.y chapter numbers across documents (chapter numbers drift with editing); use stable IDs.
  • Within the same document §x.y is allowed.
  • New stable IDs must first be registered in the §8 registry; unregistered IDs may not appear in comments, documents, or identifiers.
  • **When changing the public API/Redis key and TTL/configuration items/error codes/user-visible copy, all related documents will be synchronized in the same round. **
  • Newly added documents must be added to the scripts/docs_check/ document list of each validator, otherwise it will never be verified.

8. Stable ID registry

**The only authoritative index table. ** Always use stable IDs for cross-document references (no §x.y section numbers).

  • ID is semantically stable, unique across the warehouse, and grepable; this table registers the definition file and one-sentence description of each ID.
  • When adding a new constraint/component, first register a row in this table, and then write the **ID** tag at the definition point.
  • New IDs must be registered before appearing in comments, documents or identifiers; the validator will reject unregistered IDs.
  • When the content is moved, only the "Definition File" column in this table will be updated, and all references will be unchanged.

85 IDs in total, grouped by prefix: C-* (deployment constraints) · NG-* (non-target) · MOD-* (module) · FLOW-* (data flow) · REQ-* (requirement) · SAF-* (security) · NFR-* (non-functional) · GATE-* (gate control) · BOUND-* (boundary) · ARCH-* (architecture).

ID Definition file One sentence Category
C-DOCKER docs/deployment.md §0 Docker deployment required Deployment constraints
C-DEBIAN-SLIM docs/deployment.md §0 Debian slim, disable Alpine Deployment constraints
C-NO-SECRET-IN-IMAGE docs/deployment.md §0 secrets are not included in the image Deployment constraints
C-RUSTLS docs/deployment.md §0 rustls + native-roots Deployment constraints
C-HTTPS-INBOUND docs/deployment.md §0 HTTPS-only inbound, plaintext HTTP within the container Deployment constraints
C-HTTPS-URL docs/deployment.md §0 Public HTTPS URL is provided by the platform (bot does not hold a certificate) Deployment constraints
C-AUTH-APP-BASIC docs/deployment.md §9 / docs/design.md §3.1 Stalwart Authentication = App Password + Basic Deployment Constraints
C-NO-TCP-EXPOSE docs/deployment.md §0 Single listener PORT, no additional TCP ports exposed Deployment constraints
C-NO-LONG-CONN docs/deployment.md §0 No SSE/WS/long-polling equal-length connections Deployment constraints
C-REDIS-ONLY-STATE docs/deployment.md §0 State only external Redis, not SQLite/local volumes Deployment constraints
C-REDIS-MANAGED-AOF docs/deployment.md §0 Redis user hosting + enable AOF persistence Deployment constraints
C-PORT docs/deployment.md §0 Common PORT conventions Deployment constraints
NG-SERVER-MODE docs/deployment.md §1 RUN_MODE=server resident, non-target (this variable has been removed with Config::from_env(), this identifier is no longer in the code) non-target
NG-POLLING-SSE docs/deployment.md §1 EventSource/SSE long-lived, non-target non-target
NG-LONG-POLLING docs/deployment.md §1 Telegram long polling, non-target non-target
NG-SQLITE-PERSIST docs/deployment.md §1 SQLite persistence, non-target non-target
NG-LOCAL-VOLUME docs/deployment.md §1 Local volume persistence, non-target non-target
NG-SERVERLESS-BIND docs/deployment.md §1 Bind to specific serverless platform, non-target non-target
MOD-DEDUP docs/deployment.md §6 Redis SET NX idempotent key Components
MOD-STREAMS docs/deployment.md §6 Redis Streams queue + worker Components
MOD-SINCESTATE docs/deployment.md §6 sinceState cursor (stored in Redis) Components
FLOW-NEW-MAIL docs/design.md §5.4 / docs/deployment.md §6 Push new mail flow Data flow
FLOW-RECONCILE docs/deployment.md §6 External Cron reconciliation + Redis loss recovery Data flow
REQ-AI-CONFIRM docs/design.md §12 AI only touches text after explicit request + confirmation Requirements
REQ-VIEW-DIRECT docs/design.md §12 View original text always JMAP direct fetch Requirements
REQ-LONG-EMAIL docs/design.md §12 It is forbidden to send the full text of long emails, AI abstract ~300 words Requirements
REQ-ANALYSIS-EPHEMERAL docs/design.md §12 Analysis/summary results are not persisted Requirements
REQ-ATTACH-ONDEMAND docs/design.md §12 Pull attachments on demand Requirements
REQ-AI-FUSE docs/design.md §12 AI failed about 3 times → circuit breaker + user confirmation fallback Requirements
REQ-LLM-OPENAI-COMPAT docs/design.md §12 OpenAI-compatible environment variables Requirements
REQ-AI-EXTERNAL-CONSENT docs/design.md §12 / §3 Only send text to external AI with explicit permission from the user Requirements
REQ-AI-CONSENT docs/design.md §12.3.1 AI authorization period and Redis short-term TTL (authorization validity period, reask after expiration) Requirements
REQ-SINGLE-ACCOUNT docs/design.md §3.1/§11.3 Single account; multiple accounts = multiple bot instances Requirements
REQ-PUSH-TYPES src/domain/jmap/client.rs:110 Comments PushSubscription/set create does not have the types parameter in jmap-client 0.4.2; the subscription id must be narrowed to Email + EmailDelivery by push_subscription_update_types before being exposed to the outside world Requirements
REQ-RECONCILE-IDEMPOTENCY src/state.rs claim_dedup + get_reconcile_state / docs/design.md §8.2 JMAP reconciliation cursor is only advanced after all paging events are successfully enqueued (XADD); a single reconciliation is guaranteed to fly solo (TTL) by Redis SET NX EX lock lock:reconcile 300s, owner token is renewed for 90s, only the holder can renew/release); the processing end then goes through claim_dedup (SET NX EX, 86400s) to ensure that the same flow message is not delivered repeatedly Requirements
REQ-TIMEZONE-DISPLAY src/config.rs SUPPORTED_TIMEZONES / docs/reference.md §5.4 Notification receipt time is configured by business timezone (IANA, default Asia/Shanghai) is rendered as %Y-%m-%d %H:%M; only 16 non-daylight saving time zones are accepted, no match is returned 422, no time zone library inference Requirements
NFR-NOTIFY-SLA docs/deployment.md §6.5 Notification availability ≥99.9%, minor latency allowed Non-functional
NFR-RECONCILE-INTERVAL docs/deployment.md §6.3 External cron reconciliation interval 5–10 minutes Non-functional
SAF-NOTIFY-META §3 New email notifications only contain metadata, and the text is not included in the notification Security
SAF-CHAT-ALLOWLIST §3/docs/design.md §7.3 CHAT_ALLOWLIST hard constraint, reject non-whitelist before processing Security
SAF-AUTH-RECONCILE §3/docs/design.md §7.3 /reconcile requires Authorization: Bearer RECONCILE_TOKEN, fail-closed Security
SAF-AUTH-TG-WEBHOOK §3/docs/design.md §7.3 /webhook/tg requires header X-Telegram-Bot-Api-Secret-Token == TG_WEBHOOK_SECRET Security
SAF-AUTH-JMAP-PUSH §3 / docs/design.md §7.3 /push/jmap Press pushSubscriptionId to check the Redis short-term verification status (save session_digest digest), if it is missing or the digest does not match, it will be rejected; if the status is missing, it will fall back to worker re-verification (fail-closed) Security
SAF-ADMIN-SESSION src/config.rs session_digest / §3 Returns the SHA-256 digest used for Redis admin-session records; the bearer token itself is never written to Redis Security
SAF-NO-SECRET-ECHO src/config.rs SecretString fields and the public configuration API The secret field does not implement Debug and never enters the public configuration API response; the Redis wire format type is private and must not be serialized into the HTTP response Security
SAF-PROBE-PUBLIC §3 / docs/design.md §7.3 /healthz, /ready Public probes: no authentication, no sensitive information Security
ARCH-HEALTHZ docs/design.md §7.3/§10.0 /healthz liveness (process survival), long-term semantic stability Architecture
ARCH-READY-BASELINE docs/design.md §7.3/src/notify.rs ready+probe_jmap_session+probe_telegram_get_me /ready End-to-end ready: configuration integrity + Redis reachability + outbound read-only probe (JMAP session GET, Telegram getMe, each PROBE_TIMEOUT=3000ms, parallel, worst about 3s), all four exceed 200 (readiness report JSON contains real jmap/telegram fields), any one fails 503 (standard error envelope {"error":"service_unavailable","request_id":<id>} + Retry-After: 30); the probe is read-only, stateless write (refresh_business_config is only read from Redis), the bot token is only used to spell URL Schema
GATE-P0 docs/design.md §10.0 Stage 0 P0 gate control (fmt/clippy/test passed Debian container, etc. 8 items) Process
BOUND-STAGE1 docs/design.md §10.0 Stage 1 Pushing the Boundary (Enter after passing GATE-P0; R1 entrance authentication has been implemented in Stage 0) Process
ARCH-CONFIG-ENV docs/design.md §7.1 Configuration = environment variables manually parsed, no figment/TOML Architecture
ARCH-AXUM-08 docs/design.md §10.0 Single-port axum 0.8 entry (version is subject to Cargo.toml) Architecture
ARCH-STAGE0 docs/design.md §10.0 / §5.3 Phase 0 Actual Delivery and Current Status Architecture
ARCH-DEPS-STAGE0 docs/design.md §10.0 / §4 Stage 0 actual dependency set (axum/serde/secrecy/subtle/tokio/tracing…) Architecture
ARCH-DEPS-STAGE1 docs/design.md §10.0-1 / §4 Phase 1 dependency status: jmap-client 0.4.2 has been introduced, version/features shall be subject to Cargo.toml, WebSocket feature is disabled Architecture
ARCH-DEPS-STAGE4 docs/design.md §10.0-1 / §4 Current dependency status after stage 3.5/4: redis 0.27, reqwest 0.13 have been introduced and actually used; teloxide has not been introduced (Telegram is self-developed by src/channel.rs and implemented by reqwest); the version must be Cargo.toml Subject Architecture
MOD-JMAP-CLIENT docs/design.md §6-7 / §4 domain::jmap::client true read-only adapter (session/account/mailbox/email read-only) Components
MOD-TELEGRAM-NOTIFY src/worker.rs / docs/design.md §12 Bounded metadata notification worker: consume outbound queue, deliver masked notification via Telegram (does not carry body/AI response) Components
REQ-JMAP-SESSION-URL docs/design.md §7.1 / .env.example JMAP_SESSION_URL accepts the service base address or complete /.well-known/jmap, normalizes it to origin/base and then submits it jmap-client (no duplicate paths); The code has been implemented (D-G1-1) and has been verified on a real machine with a real account (2026-09-28: fetchChanges persistence baseline: cursor, Email/query search replies, all confirmed by the user) Requirements
SAF-JMAP-URL docs/design.md §7.1 / §4 JMAP URL constraints: HTTPS only, disallow inline credentials, deny dangerous query Security
REQ-JMAP-RAW-MULTIPART docs/design.md §3.2/§10.1 read_email Multi-part Original text: Splice the parts "with part_id and bodyValue" in text_body order; no available part → clear error Requirements
GATE-G1-JMAP-READONLY docs/design.md §10.1 / §6 G1 access control: read-only adapter Code has been implemented (mock + #[ignore] real machine test), to be real cargo test -- --ignored jmap:: verification Process
ARCH-LB-WORKER docs/deployment.md §10 Multi-instance LB/HA: a free-plan Cloudflare Worker as the only registered entrance, plus failover. The backends stay directly reachable and it adds no access control Architecture
C-LB-SINGLE-REG-URL docs/deployment.md §10.1 Telegram/Push/Cron only registers the stable URL of the Worker; the backend platform entrance does not register externally Constraints
C-LB-SHARED-SECRETS docs/deployment.md §10.3 Multiple instances must share the same set of SAF-AUTH-* secrets, otherwise a random 401 Constraints
SAF-LB-PASSTHRU docs/deployment.md §10.3 / §3 Trust model = transparent transmission: Worker does not overwrite authentication; the backend must continue fail-closed verification (the backend may be directly connected to the public network) Security
SAF-RECONCILE-LOCK docs/deployment.md §10.5 /reconcile does not fan out, Redis lock guarantees single instance execution and avoids repeated reconciliation Security
MOD-STREAMS-GROUP docs/deployment.md §10.5 Multiple instances use the same Streams consumer group name, and Redis will automatically allocate it (at-least-once will not repeat processing) Components
MOD-HEALTH-AGG docs/deployment.md §10.6 Worker aggregate health view, reporting the survival of each backend for external monitoring Components
MOD-DEBUG src/debug.rs / docs/deployment.md §2.1 / docs/design.md §7.6 / docs/reference.md §3 Remote joint debugging read-only surface: DEBUG_ENABLED (or --debug) + DEBUG_TOKEN mounted after double factor is enabled /debug/*, otherwise it will not be mounted component
SAF-DEBUG-GATE src/main.rs / src/debug.rs / docs/design.md §7.6 / docs/deployment.md §2.1 Two-factor gate: "DEBUG_ENABLED is true or command line with --debug" and DEBUG_TOKEN Routes are mounted only if they are not empty; if any are missing, they are not mounted at all (the request fails with 404), and it is absolutely closed by default. The start signal goes through env instead of argv, so that the startup command remains static, and the switch can be switched at a single point on the platform console Security
SAF-DEBUG-AUTH src/debug.rs / docs/deployment.md §2.1 After mounting /debug/* requires Authorization: Bearer DEBUG_TOKEN constant time comparison, fails with 401 and has no side effects Security
REQ-DEBUG-ENDPOINTS src/debug.rs / docs/reference.md §3 / docs/deployment.md §2.1 Endpoint contracts: GET /debug/ping, /config, /redis, /jmap, /telegram, /worker are all read-only; POST /debug/notify Send a test message through the real outbound link; the response body does not contain the original text of secret (the credential field only returns *_configured Boolean, and the non-ciphertext identity and budget fields are still returned in plain text) Requirements
SAF-DEBUG-ORIGIN-ONLY docs/deployment.md §2.1 / docs/reference.md §4 /debug/* is not among the 18 safe routes of the gateway, and the Worker will always receive 404 route not forwarded; it can only be directly connected to the backend origin, and the public network is unreachable Security
SAF-DEBUG-ALLOWLIST src/debug.rs / docs/deployment.md §2.1 POST /debug/notify only verifies chat_id when the chat whitelist** is not empty**; it will not intercept when the whitelist is not configured (empty), so to enable this page, you must confirm that the business whitelist has been configured Security
NFR-HA-MULTI-INSTANCE docs/deployment.md §10.7 / §9.1 Multi-instance high availability semantics; both active-active or active-standby; Redis single point of failure is not within the scope of the solution (user external solution) Non-functional
C-NO-DB docs/deployment.md §0 / §9.1 / §3 Production does not use any database (no SQLite/Postgres/MySQL/embedded), Redis is the only state store; the application does not connect to a second database Constraints
C-NO-LOCAL-WRITE docs/deployment.md §0 / §9.1 / §3 Disable local file/directory writes (log/data/tempcache/local volumes) Constraints
C-LOG-STDOUT-ONLY docs/deployment.md §0 / §9.1 / §3 The log only writes stdout/stderr, collected by the platform; disables the file log backend Constraints
SAF-LOG-PURITY docs/deployment.md §0 / §9.1 / §3 Log and Redis write content is limited to structured events/counts/timestamps/masked summaries; keys/email bodies/AI request responses/attachment content are prohibited Security
SAF-ENABLE-FLAG src/notify.rs put_enabled / src/state.rs config:enabled The global switch is Redis single key config:enabled; if it is not written, it is regarded as closed, and business_enabled will be treated as closed if an error occurs (fail-closed); writing requires admin-session Bearer Security
C-NO-STATEFUL-RECOVERY docs/deployment.md §0 / §9.1 / §3 It is prohibited to rely on in-process status for production recovery; all recovery will use Redis + JMAP reconciliation; in-process caching is only for performance optimization, and loss must be safe and reentrant Constraints
ARCH-STATE-REDIS docs/design.md §10.0 / Redis is the only state source The state layer uses Redis (Streams/SET NX/Lock/Abstract), and the process does not hold recoverable state Architecture
C-REDIS-EXTERNAL docs/deployment.md §8.1 Redis is provided by an external certified instance and is not in the same container as this service Deployment constraints
GATE-UPTIME-KUMA docs/deployment.md §10.1 /healthz stable semantics can be directly connected to external probes such as Uptime Kuma Access Control
GATE-DOCS scripts/docs_check/run_all.sh / docs/deployment.md Document access control: All validators must have 0 error and exit 0 to be considered passed Access control