Admin console
How admins set up model connections, manage accounts and roles, and back up and restore the system settings.
The admin console is under the 管理 (Admin) menu on the left. Only admins can see it, and its APIs accept only admins. The menu also has system status, background jobs, prompt templates and other pages; this page covers LLM settings, users and settings backup.
LLM settings
Each model connection used for analysis is called a profile. Under 管理 → LLM 設定 (Admin → LLM settings) you can set up several, then pick a pool strategy that decides which one each call goes to.
| Field | What it does |
|---|---|
| Name 名稱 | A name to tell profiles apart. |
| Provider | Ollama, or an OpenAI-compatible service. |
| Base URL | The service address. For an OpenAI-compatible service it may include /v1. |
| API Key | The list shows only the last four characters. When editing, leave it empty to keep the current key. Ollama usually doesn’t need one. |
| Model 模型 | Press 抓取 (Fetch) to list the models the service offers. |
| Enabled 啟用 | A disabled profile takes no part in analysis. |
| Allow parallel requests 允許並行請求 | When off, calls to this profile go one at a time and the rest wait in the backend. |
| Priority | For fallback, lower numbers are tried first. Default 100. |
| Weight | The load-balancing weight. Default 1. |
| Timeout (seconds) Timeout(秒) | Timeout for a single call. Default 300. |
| Temperature, Top P | Leave empty to use the service’s default. |
There are two pool strategies:
Both strategies use only enabled profiles. If one fails, the next is tried; a call fails only when all of them do.
Saved settings take effect immediately: profiles are read again for every call, so there is no backend restart. The test buttons (測試 in the list, 測試連線 in the edit dialog) send a ping and show whether the service answers and how fast.
Mind the concurrency: up to 4 tasks of a document are analyzed at once, and each task runs up to 3 per-condition cross-check calls at once. Both can be changed with ANALYSIS_CONCURRENCY and NODE_REVIEW_CONCURRENCY. For a local model that handles one request at a time, such as Ollama or llama.cpp on a single GPU, turn off parallel requests; otherwise requests pile up inside the model server with their timeouts still running, and end up timing out.
If the database has no enabled profile, the backend falls back to the Ollama set by the environment variables OLLAMA_HOST and OLLAMA_MODEL.
The prompt for every analysis stage is under 管理 → Prompt 範本 (Admin → Prompt templates); a saved version replaces the built-in one. For what each stage does, see the analysis pipeline.
Users and roles
Accounts have one of three roles:
| Role | What it can do |
|---|---|
| Admin 管理員 | Everything, including the whole admin console and unlocking locked clauses. |
| Reviewer 審核者 | Upload and analyze documents, edit tasks, review each condition and sign off on clauses, edit logic, re-analyze conditions, and send approved clauses back for re-review. |
| Viewer 檢視者 | Sees documents, the clause review page and the tools pages, but can’t upload, analyze, sign off or edit logic. |
Permissions are enforced by the backend, not just by hiding buttons: apart from the health check, sign-in and sign-out, every API requires a signed-in user, and write actions are further limited by role.
Under 管理 → 使用者 (Admin → Users) you can add accounts, change roles, reset passwords, and disable or re-enable accounts. Usernames are 2–64 letters, digits or . _ @ -, passwords are 8–128 characters, and new accounts default to reviewer.
Accounts can’t be deleted, only disabled. Review history and sign-offs record who did them, and deleting the account would destroy that history. A disabled account can’t sign in, and all its records stay.
You can’t disable your own account, and at least one enabled admin must remain.
Disabling an account or changing its role doesn’t wait for the user to sign out: every request checks whether the account is still enabled and what its role is now.
Replace the default admin before go-live: when the users table is empty, the backend creates an admin on startup. Its username and password can be set with SEED_ADMIN_USER and SEED_ADMIN_PASSWORD; otherwise it uses the development defaults.
Sign-in and lockout
After sign-in, the session is kept in an httpOnly cookie, and users must sign in again after 24 hours.
After 10 failed sign-ins for the same account from the same IP within 15 minutes, further attempts are blocked until 15 minutes have passed since the first failure, and the screen says how many minutes to wait. A successful sign-in resets the count.
The count is kept per account and IP, so someone guessing one account’s password doesn’t lock out everyone else in the same office.
Every failure is written to audit_log (auth.login_failed) with the source IP.
The count lives in the backend’s memory, so restarting the backend resets it.
Settings backup
管理 → 設定備份 (Admin → Settings backup) exports the system settings to one encrypted file that you can restore on another host or after a rebuild. The file contains:
- Every LLM profile and the pool strategy, including API keys.
- Every user account, including its argon2 password hash (not the plain password), so users keep their passwords after a restore.
- A few other system settings.
To export, set an encryption passphrase of at least 12 characters, enter it twice, and download the .cqlbak file. It is encrypted with AES-256-GCM, with the key derived from the passphrase by scrypt. The only unencrypted parts are the export time and the number of LLM profiles and accounts, so you can tell files apart.
Importing takes two steps, and the first writes nothing:
- Choose the backup file, enter its passphrase and press 解密並預覽 (Decrypt and preview). The screen shows what the file contains and what the import would delete or disable.
- If that looks right, type REPLACE and press 確認匯入並覆蓋 (Confirm import and overwrite).
An import overwrites; it doesn’t merge:
| Item | After import |
|---|---|
| LLM profiles and pool strategy | Replaced wholesale: every current profile is deleted and the file’s profiles take their place, along with its strategy. |
| Accounts in the file | Created if missing; otherwise their password hash, display name, role and enabled state are overwritten. |
| Accounts not in the file | Disabled, not deleted; their review history stays. |
| Other system settings | Overwritten with the file’s contents. |
| Prompt templates Prompt 範本 | Not in the backup; an import leaves them alone. |
The import is refused when:
- The file has no enabled admin. After the import nobody could get into the admin console.
- The passphrase is wrong, or the file is damaged or tampered with. Decryption can’t tell these apart, so both are refused.
- The backup file comes from a newer version than the running system.
- An account has no password hash or a hash that isn’t argon2, or an LLM profile lacks a name, model or Base URL.
The import runs in a single database transaction: either all of it applies or nothing changes.
Keep backup files safe: they hold working API keys and every account’s password hash. The passphrase isn’t stored anywhere in the system and can’t be recovered if lost; keep it apart from the file.