TimeTagger v26.1.3 配置参数详解

TimeTagger v26.1.3 全部 9 个配置参数的实测详解:无 --help 的参数查看方式、CLI 与环境变量双注入形式与覆盖顺序、默认值与取值语义逐项展开(bind/datadir/credentials/反代认证三件套/path_prefix/app_redirect/log_level),并给出单机自用、局域网多用户、反代统一认证三种生产模板与生效验证手段。

TimeTagger时间追踪自托管项目管理2026/10/24

TimeTagger v26.1.3 配置参数详解

目标:讲清 TimeTagger v26.1.3 的全部 9 个配置参数——查看方式(无 --help 的实测结论)、CLI 参数与环境变量两种注入形式、每个参数的默认值/取值语义/生效验证,并给出单机自用、局域网多用户、反代子路径三种生产配置模板。全部命令与回显已在 Ubuntu 22.04 服务器(TimeTagger v26.1.3,Docker Compose 部署)实测通过。

一、环境信息

  • 操作系统:Ubuntu 22.04.5 LTS
  • TimeTagger:v26.1.3(2026-02-02 发布,官方最新稳定版,定位与选型背景见《TimeTagger v26.1.3 介绍与选型指南》),依赖组件实测版本见下方 --version 回显
  • 部署形态:Docker Compose(部署过程见《TimeTagger v26.1.3 Docker Compose 部署指南》第四章),服务监听宿主机 8137 端口
  • 数据目录:/opt/timetagger-compose/_timetagger(挂载为服务内 /root/_timetagger)
  • 演示账号:admin / q1w2e3r4;参数验证时另用一次性实例演示多用户与反代参数

TimeTagger 的版本与依赖组件版本可用内置的 --version 钩子查看(这是它唯一认的查询参数):

python -m timetagger --version

实测输出:

timetagger 26.1.3
asgineer 0.9.5
itemdb 1.3.0
pscript 0.8.0

💡 说明:asgineer 是作者自研的极简 ASGI 框架,itemdb 是内嵌 SQLite 封装,pscript 负责把前端 Python 编译成 JS——四个版本号就是 TimeTagger 服务端的全部技术栈,没有外部数据库与消息队列。

二、前置条件

  • TimeTagger 已可运行(PyPI 安装或镜像部署均可;本文以镜像部署的服务为观测对象);
  • 具备向服务传入环境变量或命令行参数的手段:镜像/Compose 场景改 environment: 段,PyPI 场景改启动命令或 shell 环境变量;
  • 若要验证反代认证参数,需有一个可注入 X-Remote-User 类请求头的反向代理(本文用一次性实例直接验证配置加载与 403 行为,不依赖真实反代)。

三、参数查看与修改方式

3.1 没有 --help:配置面只能从两处看

TimeTagger 没有实现 --help。实测传入 --help 时它不报「未知参数」,而是把它当无参启动直接拉起服务:

python -m timetagger --help

实测输出(截取关键行):

[INFO 2026-10-24 09:44:20] Collected 14 assets from /root/timetagger/common
[INFO 2026-10-24 09:44:21] Compiled pscript from dialogs.py
...
[INFO 2026-10-24 09:44:21] Server is starting up
ERROR:    [Errno 98] error while attempting to bind on address ('0.0.0.0', 80): address already in use
[INFO 2026-10-24 09:44:21] Server is shutting down

⚠️ 注意:这段实测出自镜像内环境(当时服务已在 80 端口运行,故报端口占用)。它证明了两个事实:其一,--help 不被识别,未知参数不影响启动;其二,报端口冲突说明参数解析层没有拦截任何未定义参数。查参数含义的唯一官方出处是官方文档站 libapi 页与本文第三章的逐项清单。

3.2 两种注入形式:CLI 参数与环境变量

配置加载顺序为:内置默认值 → 命令行参数 → 环境变量(同名时后者覆盖前者;该顺序出自 v26.1.3 源码 _config.py 的 set_config:先应用 argv、后应用 environ)。

形式一:命令行参数(python -m timetagger <args>),支持 --参数名=值 与 --参数名 值 两种写法,参数名的下划线与连字符等价(--log_level 与 --log-level 同效):

python -m timetagger --bind=0.0.0.0:8080
python -m timetagger --datadir /var/timedata --log-level debug

以下用一次性实例验证 CLI 参数确实生效(把参数喂给配置对象后打印生效值):

python3 -c "
import sys; sys.argv=['m','--bind=0.0.0.0:9999','--log-level','debug','--datadir','/var/tt']
from timetagger._config import set_config; set_config(sys.argv, {})
from timetagger import config; print('CLI:', config.bind, config.log_level, config.datadir)
"

实测输出:

CLI: 0.0.0.0:9999 debug /var/tt

形式二:环境变量,键名为 TIMETAGGER_ + 参数名大写。以下在一次性实例中同时注入 8 个变量并打印生效值:

docker run --rm \
  -e TIMETAGGER_BIND=0.0.0.0:8080 -e TIMETAGGER_DATADIR=/srv/timetagged \
  -e TIMETAGGER_LOG_LEVEL=debug -e TIMETAGGER_PATH_PREFIX=/tracking \
  -e TIMETAGGER_APP_REDIRECT=true -e TIMETAGGER_PROXY_AUTH_ENABLED=true \
  -e TIMETAGGER_PROXY_AUTH_TRUSTED='127.0.0.1,10.89.0.0/16' \
  -e TIMETAGGER_PROXY_AUTH_HEADER='X-Forwarded-User' \
  ghcr.io/almarklein/timetagger:v26.1.3 python -c "
from timetagger import config
for k in ['bind','datadir','log_level','proxy_auth_enabled','proxy_auth_trusted','proxy_auth_header','path_prefix','app_redirect']:
    print(k, '=', repr(getattr(config, k)))
"

实测输出:

bind = '0.0.0.0:8080'
datadir = '/srv/timetagged'
log_level = 'debug'
proxy_auth_enabled = True
proxy_auth_trusted = '127.0.0.1,10.89.0.0/16'
proxy_auth_header = 'X-Forwarded-User'
path_prefix = '/tracking/'
app_redirect = True

两处值得注意的自动规范化(实测可见):

  • TIMETAGGER_PATH_PREFIX=/tracking 被补成 /tracking/——前缀会自动补首尾斜杠,写法容错;
  • TIMETAGGER_APP_REDIRECT=true 识别为布尔 True——布尔参数接受 true/yes/on/1(不区分大小写),其余按假值处理,显式假值还额外接受 false/off/no/n/0。

环境变量只在进程启动时读取一次,改完必须重启进程才生效(镜像场景 docker compose up -d 重建,见第十章 Q1)。

3.3 查看当前生效值

运行中的服务没有「打印配置」的管理端点,确认生效值有三个实用手段:

# 手段一:同环境打印配置对象(与运行实例同环境变量时结果一致)
docker exec timetagger-v26.1.3-web python -c "
from timetagger import config
for k in ['bind','datadir','log_level','credentials','proxy_auth_enabled','proxy_auth_trusted','proxy_auth_header','path_prefix','app_redirect']:
    print(k, '=', repr(getattr(config, k)))
"

实测输出(本文实验实例未注入额外变量,即默认值):

bind = '127.0.0.1:8080'
datadir = '~/_timetagger'
log_level = 'info'
credentials = ''
proxy_auth_enabled = False
proxy_auth_trusted = '127.0.0.1'
proxy_auth_header = 'X-Remote-User'
path_prefix = '/timetagger/'
app_redirect = False
# 手段二:行为验证——status 端点探路径前缀
curl -s -w ' [%{http_code}]' http://localhost:8137/timetagger/status

实测输出:

ok [200]
# 手段三:行为验证——根路径重定向看 app_redirect / path_prefix
curl -s -o /dev/null -w '%{http_code} Location=%{redirect_url}\n' http://localhost:8137/

实测输出:

307 Location=http://localhost:8137/timetagger/

四、参数总览

v26.1.3 全部 9 个参数一览(默认值取自 3.3 实测,语义出处为官方文档 libapi 页与源码 _config.py 注释,2026-10-24 核对):

参数环境变量默认值类型一句话语义
bindTIMETAGGER_BIND127.0.0.1:8080字符串监听地址与端口
datadirTIMETAGGER_DATADIR~/_timetagger字符串数据目录(JWT 密钥 + 用户库)
log_levelTIMETAGGER_LOG_LEVELinfo字符串应用日志级别
credentialsTIMETAGGER_CREDENTIALS空字符串「用户名:BCrypt哈希」凭据表
proxy_auth_enabledTIMETAGGER_PROXY_AUTH_ENABLEDfalse布尔启用反向代理头认证
proxy_auth_trustedTIMETAGGER_PROXY_AUTH_TRUSTED127.0.0.1字符串信任的反代 IP/CIDR 列表
proxy_auth_headerTIMETAGGER_PROXY_AUTH_HEADERX-Remote-User字符串携带用户名的请求头名
path_prefixTIMETAGGER_PATH_PREFIX/timetagger/字符串应用挂载的 URL 前缀
app_redirectTIMETAGGER_APP_REDIRECTfalse布尔根路径直接跳应用(跳过落地页)

💡 说明:参数面非常收敛——没有数据库连接(存储内嵌 SQLite)、没有缓存/线程池/上传限额这类运行时参数。调参空间小是刻意设计:官方把 TimeTagger 定位成「自托管轻量时间追踪器」,性能与资源面由 Python 异步栈单进程承担。参数非法时的统一行为是抛 RuntimeError: Could not set config.<参数名>: <原因> 并拒绝启动(源码 _config.py 解析层行为)。

五、核心参数分类详解

5.1 bind:监听地址与端口

决定服务绑定的 <地址>:<端口>。默认值 127.0.0.1:8080 是 v24.12.2 安全修复后的产物(该版本前默认 0.0.0.0:80,官方在安全事件后收紧默认并敦促公网用户升级)——即「装完只听本机回环」,想让局域网/反代访问必须显式改绑:

  • 单机自用(本机浏览器访问):保持默认 127.0.0.1:8080 即可,且此时享受免凭据的 localhost 单用户模式(见 5.4);
  • 镜像/Compose 部署:必须 TIMETAGGER_BIND=0.0.0.0:80,否则端口映射出去无监听(部署篇常见问题 Q2 的根因);
  • 反代本机回环部署:bind=127.0.0.1:8080 + Nginx proxy_pass http://127.0.0.1:8080,是安全面最优的组合——应用端口不对外暴露。

改绑验证:绑定 0.0.0.0:80 的实例 curl http://localhost:8137/timetagger/status 返回 ok [200](3.3 手段二实测)。

5.2 datadir:数据目录

一切状态的落点,结构实测如下:

find /opt/timetagger-compose/_timetagger -maxdepth 2 | sort

实测输出:

/opt/timetagger-compose/_timetagger
/opt/timetagger-compose/_timetagger/jwt.key
/opt/timetagger-compose/_timetagger/users
/opt/timetagger-compose/_timetagger/users/admin~YWRtaW4=.db

逐项说明:

  • jwt.key:JWT 签名密钥(HS256),首次启动自动生成。它是登录态的根——删除/更换后所有已签发令牌立即失效。备份必须含它,否则恢复后全员重新登录;
  • users/<用户名>~base64(用户名).db:每用户一个 SQLite 库(admin → admin~YWRtaW4=.db,YWRtaW4= 即 admin 的 base64),时间记录与报表设置都在库内。新用户首次登录时自动建库。

默认值 ~/_timetagger 中的 ~ 展开为运行账户的 home(root 变体容器内即 /root/_timetagger;nonroot 变体官方模板固定用 /opt/_timetagger)。挂载规范与权限要点见部署篇第八章。

5.3 credentials:凭据表

格式 用户1:哈希1,用户2:哈希2,分隔符逗号与分号均可(源码解析层先把 ; 归一为 ,)。哈希为 BCrypt,官方生成页 https://timetagger.app/cred (页面可离线使用)。本文实验环境验证了「逗号 + 分号混排」的多用户注入:admin:<哈希>,ops:<哈希> 双用户均能用各自口令换取令牌(实测 ops 账号登录返回 {"token": "eyJ..."} 且 HTTP 200)。

三条使用红线:

  1. 哈希含 $,注入时要防插值:.env 单引号、compose 文件内 $$ 转义、docker run 单引号——三种形态的排障实录见部署篇 Q1;
  2. 凭据表只在启动时解析一次,加用户要重启;
  3. 明文口令不落盘:落盘的只有 BCrypt 哈希;credentials 是服务端唯一收口令校验的参数,校验逻辑用 bcrypt.checkpw 慢哈希比对(源码 __main__.py),非明文比对。

💡 技术拓展:Web 应用的口令存储方案对比——

方案原理取舍
明文/可逆加密口令可直接还原泄库即泄口令,禁止
SHA-256 等快速哈希单轮杂凑GPU 暴破成本低,不够
BCrypt基于成本因子的慢哈希 + 盐TimeTagger 采用;抗暴破
Argon2id内存困难型哈希更现代,但生态接入少

TimeTagger 选 BCrypt(盐自动内嵌在哈希串中),对自托管工时工具的威胁模型足够;应用本身不带账户锁定/限流,公网暴露时建议靠反代层做访问控制。

5.4 proxy_auth_enabled / trusted / header:反代头认证三件套

三参数配合工作:enabled 打开后,应用自己不管口令,信任反向代理在 header 指定的请求头里带来的用户名。三个参数的联动行为实测与源码语义如下:

  • proxy_auth_enabled=true 时,localhost 单用户模式同时被禁用(源码明确 forbidden: disabled when proxy auth is available)——防认证旁路;
  • 未启用时走代理认证登录返回 403 forbidden: proxy auth is not enabled;
  • 启用后,请求源 IP 不在 proxy_auth_trusted 列表内返回 403 forbidden: the proxy is not trusted;信任列表支持 CIDR 网段(本文实测注入 127.0.0.1,10.89.0.0/16 正常解析);
  • 请求头里没有用户名返回 403 forbidden: no proxy user provided;
  • 已登录令牌与代理头用户名不一致时校验失败(Autheticated user does not match proxy user)——防止换了代理身份还沿用旧令牌。

三件套适合已有统一认证网关(Authelia/Authentik/Nginx basic auth 等)的内网环境:应用零口令管理、凭据轮换在网关层完成。典型组合见第九章模板三。

5.5 path_prefix:URL 路径前缀

应用挂载的 URL 前缀,默认 /timetagger/。行为实测(一次性实例配 TIMETAGGER_PATH_PREFIX=/tracking 启动):

# 新前缀生效
curl -s -w ' [%{http_code}]' http://localhost:8138/tracking/status

实测输出:

ok [200]
# 旧前缀立即失效
curl -s -w ' [%{http_code}]' http://localhost:8138/timetagger/status

实测输出:

only serving at /tracking/ [404]

要点:

  • 首尾斜杠可省略,服务端自动补齐为 /tracking/(3.2 实测可见);
  • 改前缀后旧前缀整体 404,反代/书签/客户端 API 地址要同步改;
  • 默认值 /timetagger/ 的设计意图是「服务工作进程不干扰同机其他内容」(源码注释原文),子路径挂载到反代下是其常规用法;
  • API 基路径随前缀联动:<前缀>api/v2/...(API 端点全表见《TimeTagger v26.1.3 API 使用详解》)。

5.6 app_redirect:根路径直达应用

默认 false:根路径 / 返回 307 跳转到 path_prefix(落地宣传页,含演示入口与项目链接);置 true 后根路径直接 307 跳到 <path_prefix>app/ 跳过落地页。实测对照(一次性实例 app_redirect=true + path_prefix=/tracking):

307 Location=http://localhost:8138/tracking/app/

而本文实验实例(app_redirect=false,默认前缀)根路径实测为 307 Location=http://localhost:8137/timetagger/。个人自用建议置 true 少点一次;对外展示或多应用共存的门户场景保留默认。

5.7 log_level:日志级别

默认 info。控制 timetagger 与 asgineer 两个 logger(不含 uvicorn——其级别在启动层固定为 warning,源码 __main__.py)。info 级别的启动日志实测(部署篇 4.3 全文引用过)含资产收集与 pscript 编译逐文件 INFO 行,日常足够;排障时临时改 debug 重建观察。日志经 stdout 输出,镜像场景统一用 docker compose logs 查看。

六、生产配置模板

模板一:单机自用(PyPI 安装,1 台笔记本/小服务器,512MB 内存足够)

# 不设任何变量即默认配置:绑定 127.0.0.1:8080,localhost 免凭据单用户
python -m timetagger

适用:本机浏览器使用、不对外。零配置面是 TimeTagger 对个人场景的最大善意。

模板二:局域网多用户(Compose,210 人团队,12GB 内存服务器)

    environment:
      - TIMETAGGER_BIND=0.0.0.0:80
      - TIMETAGGER_DATADIR=/root/_timetagger
      - TIMETAGGER_LOG_LEVEL=info
      - TIMETAGGER_CREDENTIALS=${TT_CREDENTIALS}
      - TIMETAGGER_APP_REDIRECT=true

凭据表放 .env(多组「用户:哈希」逗号分隔),数据目录 bind mount,升级走「备份 → 换 tag → pull → up -d」(部署篇第十章)。所有成员共享同一数据空间,适合互信小团队。

模板三:反代子路径 + 统一认证(Nginx/Authelia 网关后)

    environment:
      - TIMETAGGER_BIND=0.0.0.0:80
      - TIMETAGGER_DATADIR=/root/_timetagger
      - TIMETAGGER_LOG_LEVEL=info
      - TIMETAGGER_PROXY_AUTH_ENABLED=true
      - TIMETAGGER_PROXY_AUTH_TRUSTED=127.0.0.1
      - TIMETAGGER_PROXY_AUTH_HEADER=X-Remote-User
      - TIMETAGGER_PATH_PREFIX=/time/
      - TIMETAGGER_APP_REDIRECT=true

反代只监听本机回环转发(TRUSTED=127.0.0.1),认证全部收口在网关;PATH_PREFIX=/time/ 时 Nginx 侧对应 location /time/ 转发。该模板下应用自身不存任何口令。

七、常见问题

Q1:改了环境变量,服务行为没变?

环境变量只在进程启动时读取(_config.py 在模块导入时执行一次 set_config)。Compose 场景 docker compose restart 不会重新计算 environment:——需要 docker compose up -d 让 Compose 重建实例;docker run 场景必须删旧起新。

Q2:想看帮助,--help 却把服务拉起来了?

TimeTagger 没有 --help(3.1 实测:未知参数不拦截,--help 等同无参启动,端口冲突时还可能报 address already in use)。参数含义以官方 libapi 文档与本文第四章为准;版本查询用 python -m timetagger --version。

Q3:path_prefix 写 /tracking 还是 /tracking/ 都行?斜杠规则是什么?

都行,服务端自动补首尾斜杠(3.2 实测 /tracking 生效为 /tracking/)。唯一非法形态是空串。改前缀后旧前缀整体 404(5.5 实测),注意同步反代与客户端。

Q4:启用反代认证后,原来 localhost 免登录进不去了?

设计如此:proxy_auth_enabled=true 会禁用 localhost 单用户模式(防旁路,见 5.4)。恢复本地调试可用性的办法是临时关掉该变量重启,或把本机 IP 加入 TRUSTED 并让本机反代注入用户头。

Q5:凭据明明写对了,登录仍报 Invalid credentials [403]?

九成是哈希里的 $ 在注入链路被插值。三种形态的排查(.env 单引号 / compose $$ / docker run 单引号)与验证命令 docker exec <实例> env | grep CREDENTIALS 见部署篇 Q1。剩余一成是哈希与口令不匹配——重新在 https://timetagger.app/cred 生成。

Q6:数据目录想挪到大盘分区,直接改 TIMETAGGER_DATADIR 行吗?

行,但要把旧目录整体搬过去(jwt.key + users/ 都要),否则旧令牌全部失效、历史记录「消失」(其实是新库)。镜像场景记得同步改 volume 挂载点,两处必须一致(5.2)。

八、参考链接