Skip to content

Configuration vi

SkimMail docs edited this page Sep 18, 2026 · 8 revisions

English · Tiếng Việt · 中文

Configuration

Từ 1.9.0, cấu hình SkimMail chia thành ba "mặt phẳng" (plane) — ba chỗ khác nhau mà một setting có thể sống, mỗi chỗ có vòng đời riêng và cách đổi riêng. Biết một setting nằm ở plane nào cho bạn biết chính xác phải đổi nó ở đâu và khi nào thay đổi đó có hiệu lực. Quy tắc quan trọng nhất, chỉ một câu: cái gì bạn đặt trong environment luôn thắng.

Ba mặt phẳng (plane)

Plane 0 — Bootstrap

Đúng năm key: DATA_DIR, DB_DRIVER, DATABASE_URL, KEY_PROVIDER, LISTEN_ADDR. Chúng tồn tại để trả lời đúng một câu hỏi — database nằm ở đâu, và mở nó bằng cách nào? — câu hỏi phải được trả lời trước khi có database để lưu bất cứ thứ gì khác.

  • DATA_DIR chỉ đặt được qua environment, vĩnh viễn. Nó không thể nằm ở đâu khác, vì nó chính là tên thư mục chứa mọi thứ khác (kể cả file nói ở dưới) sống trong đó.
  • Bốn key còn lại có thể đặt qua environment variable, hoặc ghi vào <DATA_DIR>/bootstrap.env — một file nhỏ do chính SkimMail quản lý, qua bước chọn database của setup wizard lần đầu hoặc qua skimmail config set. File này tồn tại chính là để người cài bằng một dòng curl | sh rồi muốn dùng Postgres có chỗ để đặt DSN mà không phải tự tay sửa một systemd EnvironmentFile.
  • Thứ tự ưu tiên: environment thắng file, file thắng giá trị mặc định sẵn có.
  • Thay đổi ở đây có hiệu lực sau lần restart tiếp theo — không có gì ở đây được nạp lại khi đang chạy (live-reload), vì nó quyết định mở tiến trình database nào ngay từ đầu.

Ví dụ cụ thể. Một bản apt mới cài, không đặt DATABASE_URL trong /etc/default/skimmail, sẽ chạy SQLite tại /var/lib/skimmail/skimmail.db (mặc định sẵn có). Trỏ setup wizard lần đầu vào một Postgres sẽ ghi DB_DRIVER=postgresDATABASE_URL=postgres://… vào /var/lib/skimmail/bootstrap.env rồi restart tiến trình để dùng nó. Nếu sau đó bạn thêm DATABASE_URL=… thẳng vào /etc/default/skimmail, environment giờ thắng — giá trị trong bootstrap.env bị bỏ qua từ lúc đó, dù nó vẫn còn nằm trong file.

Plane 1 — Instance configuration

Những setting quyết định SkimMail này hiện diện trên mạng ra sao: public URL, base path, có tin một reverse proxy hay không, các origin CORS/CSRF phụ thêm, chế độ đăng nhập, và OAuth app credentials của Google/Microsoft. Trước 1.9.0, mỗi cái này là một environment variable chỉ đổi được bằng cách sửa file rồi restart; giờ chúng lưu trong database và chỉnh được từ Settings trên trình duyệt, hoặc từ shell bằng skimmail config.

Setting Key trong skimmail config Environment variable
Public URL base_url BASE_URL
Base path (phục vụ dưới subpath) base_path BASE_PATH
Tin một reverse proxy trust_proxy TRUST_PROXY
Origin CORS/CSRF phụ thêm trusted_origins TRUSTED_ORIGINS
Chế độ đăng nhập auth.mode AUTH_MODE
Google OAuth client ID / secret oauth.google.client_id / oauth.google.client_secret GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET
Microsoft OAuth client ID / secret oauth.microsoft.client_id / oauth.microsoft.client_secret MICROSOFT_CLIENT_ID / MICROSOFT_CLIENT_SECRET
  • Thứ tự ưu tiên: environment luôn thắng, và thắng tuyệt đối. Một field mà environment cung cấp sẽ hiện locked trên trình duyệt, nêu đúng tên biến cần xoá nếu bạn muốn quản lý nó từ app thay vì từ environment. Một lần ghi vào field đang locked — dù từ trình duyệt hay từ skimmail config set — đều bị từ chối, không phải bị âm thầm ghi đè.
  • Thay đổi có hiệu lực ngay lập tức — không cần restart. Server giữ cấu hình đã resolve sau một con trỏ mà nó hoán đổi mỗi lần lưu, đó là lý do vì sao sửa OAuth app hay public URL từ Settings có tác dụng ngay mà không cần restart.
  • OAuth client secret được mã hoá at-rest bằng master key (xem Security) và không bao giờ được trả về qua bất kỳ lần đọc nào — API và skimmail config list chỉ báo được là secret có được đặt hay không.
  • Chế độ đăng nhập có thêm một quy tắc riêng: một khi đã có credential, trình duyệt không thể đổi nó nữa — chỉ skimmail config set auth.mode … trên host mới đổi được, và đặt về none còn đòi thêm SKIMMAIL_ALLOW_AUTH_NONE=1 trong environment. Một admin session bị đánh cắp không thể tự tắt xác thực.

Ví dụ cụ thể. Bạn đặt BASE_URL=https://mail.example.com trong /etc/default/skimmail vì bạn quản lý instance này theo kiểu khai báo (declarative). Settings ▸ (setup/about) giờ hiện Public URL ở trạng thái locked, nêu tên BASE_URL là biến cần xoá nếu bạn muốn quản lý nó từ trình duyệt thay vào đó. Cho tới khi bạn xoá nó ở đó và restart, mọi lần thử sửa — từ trình duyệt hay skimmail config set base_url … — đều bị từ chối với cùng một thông báo.

Plane 2 — App settings

Các nút chỉnh runtime cho từng tính năng cụ thể: thời gian chờ trước khi screen lock và thời lượng session (Security), concurrency/rate/depth của sync và hành vi IDLE/poll (Sync), log level và xoay vòng file log (Logs). Những cái này có từ trước công việc cấu hình 1.9.0 và theo quy tắc đơn giản hơn, một chiều:

  • Environment variable chỉ cung cấp giá trị mặc định ở lần đầu tiên — trước khi setting đó từng được lưu lần nào.
  • Ngay khi bạn lưu một thay đổi từ tab Settings tương ứng, giá trị đã lưu thắng từ đó về sau — kể cả qua mọi lần restart sau này — bất kể environment variable còn được đặt hay không. Không có chỉ báo "locked" ở đây, vì không có gì để lock: một khi đã lưu, environment variable đơn giản không còn được tham khảo lại cho key đó nữa.
  • Những setting này không nằm trong phạm vi skimmail config — lệnh đó chỉ là lối thoát hiểm cho Plane 0 và Plane 1.

Ví dụ cụ thể. AUTO_LOCK_MINUTES chưa đặt, nên screen lock mặc định tắt (mặc định sẵn có là 0, không bao giờ khoá). Bạn bật nó lên và đặt 15 phút trong Settings ▸ Security ▸ Timeouts. Con số 15 đó giờ nằm trong database và áp dụng ở mọi lần restart. Đặt AUTO_LOCK_MINUTES=30 trong environment sau đó không đổi được gì — giá trị bạn đã lưu đã thắng rồi, và tiếp tục thắng cho tới khi bạn đổi lại từ chính tab Settings đó.

Có vài key trông như thuộc về một tính năng ở trên nhưng thật ra chỉ đọc lúc khởi động, hết — không có khoá kiểu Plane-1, không có ghi đè kiểu Plane-2, không bao giờ: TRUSTED_PROXIES (xem Security), và LOG_FILE/LOG_FORMAT (đường dẫn log cố tình không bao giờ đặt được qua API — làm vậy sẽ thành ghi file tuỳ ý từ trình duyệt).

Bảng biến môi trường

Cột "Plane" nói thay đổi đã lưu (nếu có) nằm ở đâu; cột mô tả nói environment variable làm gì với plane đó.

Plane 0 — bootstrap

Biến Làm gì Mặc định
DATA_DIR Nơi database, blob và master key sống ./data
DB_DRIVER sqlite | postgres | mysql sqlite
DATABASE_URL Connection string khi DB_DRIVER khác sqlite (rỗng)
KEY_PROVIDER file (master key thô trên đĩa) | passphrase (đã bọc) file
LISTEN_ADDR Địa chỉ HTTP server lắng nghe :8080

Plane 1 — instance configuration (environment khoá field)

Biến Làm gì Mặc định
BASE_URL Public URL — OAuth redirect, WebSocket origin, PWA manifest (rỗng)
BASE_PATH Phục vụ app dưới một subpath, ví dụ /mail /
AUTH_MODE passphrase | users | none passphrase
TRUST_PROXY Tin X-Forwarded-* từ các hop proxy được công nhận false
TRUSTED_ORIGINS Origin CORS/CSRF phụ thêm, cách nhau bằng dấu phẩy (rỗng)
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET OAuth app Google của riêng bạn, cho đăng nhập Gmail (rỗng)
MICROSOFT_CLIENT_ID / MICROSOFT_CLIENT_SECRET Azure app của riêng bạn, cho đăng nhập Outlook (rỗng)

Đăng ký hai redirect URI trên mỗi ứng dụng OAuth, không phải một: <BASE_URL>/api/oauth/callback cho cách thường, và http://localhost:8642 cho cách dán tay mà các instance không có địa chỉ HTTPS công khai dựa vào. Cả hai đều hiện kèm nút Copy dưới Settings ▸ About ▸ Setup health ▸ OAuth. Cái thứ hai không phải một setting và không có biến môi trường nào — không có gì lắng nghe ở đó, nó chỉ là một chuỗi mà nhà cung cấp cần nhận ra. Xem mục "Thêm một tài khoản Gmail hoặc Outlook (OAuth)" ở Accounts. Các bước cụ thể trên console để đăng ký ứng dụng ở Google và Microsoft, xem Register the OAuth application.

Cổng bảo mật chỉ-đọc-từ-environment (không khoá, không ghi đè — luôn là environment)

Biến Làm gì Mặc định
SKIMMAIL_ALLOW_AUTH_NONE Bắt buộc phải có, cùng với auth.mode=none, trước khi xác thực thật sự bị tắt được false (1/true để bật)
SKIMMAIL_SKIP_CLAIM Bỏ qua cổng claim code lần đầu (xem Security) false
TRUSTED_PROXIES Danh sách CIDR (cách nhau bằng dấu phẩy) được tin là hop của X-Forwarded-For (rỗng = chỉ loopback/private)

Plane 2 — app settings (environment chỉ là giá trị khởi tạo)

Biến Làm gì Mặc định
AUTO_LOCK_MINUTES Số phút không hoạt động trước khi client tự khoá màn hình; 0 = không bao giờ 0
SESSION_TTL_HOURS Thời lượng session đăng nhập; 0 = không bao giờ hết hạn 720 (30 ngày)
BACKGROUND_POLL_INTERVAL Chu kỳ poll dự phòng cho mỗi tài khoản 5m
MAX_CONCURRENT_SYNCS Số mailbox sync cùng lúc 4
SYNC_RATE_PER_MIN Số lần bắt đầu sync tối đa mỗi phút 10
SYNC_DEPTH_DAYS Cửa sổ sync header tính theo ngày; 0 = toàn bộ lịch sử 30
SYNC_MAX_RETRIES Số lần lỗi liên tiếp trước khi tự dừng sync một tài khoản; 0 = không bao giờ 3
CACHE_MAX_SIZE_MB Dọn bớt nội dung thư đã cache khi vượt tổng này; 0 = không giới hạn 2048
BODY_TTL_DAYS Xoá nội dung thư đã cache cũ hơn số ngày này; 0 = không bao giờ hết hạn 90
CLIENT_IDLE_GRACE Khoảng thời gian không có client trước khi idle-downscale 30m
IDLE_DOWNSCALE_WHEN_NO_CLIENT Hạ IDLE→poll khi không có trình duyệt nào mở false
MAX_IDLE_CONNECTIONS Giới hạn số kết nối IMAP IDLE cùng lúc; 0 = không giới hạn 0
LOG_LEVEL debug | info | warn | error info
LOG_MAX_SIZE_MB Xoay vòng file log ở kích thước này 10
LOG_MAX_BACKUPS Số file log đã xoay vòng được giữ lại 3

CACHE_MAX_SIZE_MBBODY_TTL_DAYS đúng nghĩa là Plane 2: environment cấp giá trị khởi tạo, còn giá trị bạn lưu từ Settings ▸ Security ▸ Cache nội dung thư — hoặc qua PUT /api/settings/cache — được ghi vào database và thắng từ đó trở đi. Lưu ý đây là chiều ngược với Plane 1, nơi đặt biến là khoá luôn ô nhập: ở đây environment nhường cho giá trị đã lưu, nên panel đó không hiện gợi ý "environment đang giữ quyền" nào cả.

Panel này có từ 1.11.1. Ở 1.11.0 nó đã được dựng nhưng đặt trong tab Storage đang ẩn, nên không bản cài nào mở được, và khi đó environment thật sự là con đường duy nhất. Xem Cache nội dung thư.

Chỉ đọc lúc khởi động, không có UI hay CLI nào ghi đè được

Biến Làm gì Mặc định
MAX_CONNS_PER_ACCOUNT Số kết nối IMAP cùng lúc cho mỗi tài khoản (giới hạn 1–14) 10
BLOB_ENCRYPT Mã hoá nội dung thư đã cache trên đĩa (AES-256-GCM, khoá dẫn xuất từ master key). Đọc thì luôn giải mã, nên tắt đi không bao giờ làm mất cache đang có. Luôn bật trừ khi bạn tắt tường minh: chỉ false, 0, nooff mới tắt (không phân biệt hoa thường, bỏ qua khoảng trắng hai đầu). Mọi giá trị khác — 1, TRUE, yes, on, hay một lỗi gõ — đều để mã hoá bật true
LOG_FILE Đường dẫn file log xoay vòng; rỗng = chỉ stdout/journal (rỗng; apt tự đặt /var/log/skimmail/skimmail.log)
LOG_FORMAT text (key=value) | json text
FIREBASE_ENABLED / FIREBASE_CREDENTIALS Push trên di động qua Firebase (FCM), tuỳ chọn; hầu hết người tự host không cần cái này — Web Push trên trình duyệt hoạt động mà không cần cấu hình gì false / (rỗng)
VAPID_PUBLIC / VAPID_PRIVATE / VAPID_SUBJECT Ghi đè cặp khoá Web Push được tự sinh (rỗng — một cặp khoá được tự sinh và lưu lại)
UPDATE_FEED_URL Ghi đè release feed cho self-update (dùng cho mirror air-gapped) (rỗng = GitHub Releases)
UPDATE_PUBKEY Khoá công khai minisign dùng xác minh self-update và chữ ký manifest plugin. Bản build chính thức đã gắn sẵn; chỉ đặt biến này trên bản build tuỳ chỉnh (rỗng trên bản build tuỳ chỉnh = update chỉ dừng ở thông báo, manifest plugin không được xác minh)
REVOCATION_FEED_URL Ghi đè nơi lấy danh sách license-id đã bị thu hồi (rỗng = feed mặc định)
SKIMMAIL_PLUGINS_URL Ghi đè nơi tải plugins.json (danh mục plugin Remote access / WireGuard engine) (rỗng = manifest GitHub Pages mặc định)

BLOB_ENCRYPT là boolean duy nhất trong bảng này mặc định bật, và cũng là cái duy nhất coi một giá trị không nhận diện được là "bật". Mọi công tắc khác ở đây mặc định tắt, nơi việc không hiểu một giá trị là chiều vô hại; một cờ mặc định bật thì ngược lại, nên nó phải hỏng về phía vẫn mã hoá.

Nếu bạn đang chạy 1.11.0, hãy kiểm tra biến này. Bản đó so giá trị với đúng chữ true, nên BLOB_ENCRYPT=1, =yes, =on=TRUE đều đọc ra tắt và lặng lẽ ngừng mã hoá nội dung thư mới. Đã sửa ở 1.11.1. Những nội dung đã cache trong lúc bị tắt không được ghi đè tại chỗ; chúng nằm nguyên không mã hoá cho tới khi rời khỏi cache. Hạ giới hạn dung lượng hoặc tuổi trong Settings ▸ Security sẽ đẩy chúng ra, và chúng quay lại ở dạng đã mã hoá.

Công cụ AI là mảng tính năng duy nhất đã compile sẵn vào binary mà tab Settings của nó vẫn bị ẩn ở 1.15.0 — environment variable của nó cố tình bị bỏ khỏi bảng này cho tới khi tab đó được bật lại. Công tắc storage engine / S3 blob trước đây bị ẩn cùng với nó và đã hiện từ 1.14.0, nên các thiết lập của nó giờ nằm ở Settings ▸ Storage. Các biến body cache ở trên từng chung số phận ấy sớm hơn; từ 1.11.1 panel của chúng nằm ở Settings ▸ Security. Xem Home để biết cái gì đang bị ẩn và vì sao.

Lối thoát hiểm từ shell: skimmail config

Mọi thứ Plane 1 cho làm trên trình duyệt, skimmail config cũng làm được trên host — cộng thêm một việc trình duyệt không làm được một khi đã có credential: đổi chế độ đăng nhập.

skimmail config list                              # mọi setting, giá trị, và nó đến từ đâu
skimmail config get base_url                       # một giá trị, in trần, để dùng trong script
skimmail config set base_url https://mail.example.com
skimmail config unset base_url                      # xoá nó
skimmail config export                              # khối env tương ứng với những gì đang trong database

list in setting của Plane 1 và các key bootstrap của Plane 0 thành hai bảng riêng (đổi bootstrap cần restart mới có hiệu lực; đổi Plane 1 thì không, và lệnh có nói rõ điều đó). export bỏ qua secret — chúng đã mã hoá at-rest và không đọc ngược lại được; lệnh in ra một dòng comment nhắc bạn dán lại secret bằng tay. Thử set một key mà environment đã sở hữu sẽ lỗi với cùng thông báo "xoá nó ở environment trước" mà trình duyệt hiển thị.

Đây cũng là đường quay lại nếu bạn từng tự khoá mình khỏi chế độ đăng nhập trên trình duyệt, và là nơi duy nhất auth.mode đổi được một khi đã có tài khoản.

Thứ không bao giờ được biến thành một config file

bootstrap.env cố tình rất hẹp — DB_DRIVER, DATABASE_URL, KEY_PROVIDERLISTEN_ADDR, không hơn (DATA_DIR không bao giờ có thể nằm trong đó: chính nó là nơi file này sống) — và Plane 1 không bao giờ có thêm một file riêng song song với database. Cho cùng một setting hai "nhà" đều ghi được độc lập (một file một dòng database, cả hai đều tự nhận là nguồn chân lý) chính là cách một dự án tự host khác từng có một admin UI âm thầm không lưu được thay đổi, vì một config file mà UI không hề biết tới cứ lặng lẽ thắng mỗi lần. Quy tắc của SkimMail giữ đơn giản vì nó chỉ cần đúng theo một chiều: environment thắng tất cả, và mọi thứ khác chỉ sống ở đúng một chỗ duy nhất.


SkimMail · skimmail@base101.app · 2026-09-18 · commit f525934

Clone this wiki locally