TimeTagger v26.1.3 配置参数详解
TimeTagger v26.1.3 全部 9 个配置参数的实测详解:无 --help 的参数查看方式、CLI 与环境变量双注入形式与覆盖顺序、默认值与取值语义逐项展开(bind/datadir/credentials/反代认证三件套/path_prefix/app_redirect/log_level),并给出单机自用、局域网多用户、反代统一认证三种生产模板与生效验证手段。
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 核对):
| 参数 | 环境变量 | 默认值 | 类型 | 一句话语义 |
|---|---|---|---|---|
bind | TIMETAGGER_BIND | 127.0.0.1:8080 | 字符串 | 监听地址与端口 |
datadir | TIMETAGGER_DATADIR | ~/_timetagger | 字符串 | 数据目录(JWT 密钥 + 用户库) |
log_level | TIMETAGGER_LOG_LEVEL | info | 字符串 | 应用日志级别 |
credentials | TIMETAGGER_CREDENTIALS | 空 | 字符串 | 「用户名:BCrypt哈希」凭据表 |
proxy_auth_enabled | TIMETAGGER_PROXY_AUTH_ENABLED | false | 布尔 | 启用反向代理头认证 |
proxy_auth_trusted | TIMETAGGER_PROXY_AUTH_TRUSTED | 127.0.0.1 | 字符串 | 信任的反代 IP/CIDR 列表 |
proxy_auth_header | TIMETAGGER_PROXY_AUTH_HEADER | X-Remote-User | 字符串 | 携带用户名的请求头名 |
path_prefix | TIMETAGGER_PATH_PREFIX | /timetagger/ | 字符串 | 应用挂载的 URL 前缀 |
app_redirect | TIMETAGGER_APP_REDIRECT | false | 布尔 | 根路径直接跳应用(跳过落地页) |
💡 说明:参数面非常收敛——没有数据库连接(存储内嵌 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+ Nginxproxy_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)。
三条使用红线:
- 哈希含
$,注入时要防插值:.env单引号、compose 文件内$$转义、docker run单引号——三种形态的排障实录见部署篇 Q1; - 凭据表只在启动时解析一次,加用户要重启;
- 明文口令不落盘:落盘的只有 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)。
八、参考链接
- 配置参数官方文档(libapi):https://timetagger.readthedocs.io/en/latest/libapi/
- 凭据哈希生成页:https://timetagger.app/cred
- 官方仓库(
_config.py/__main__.py源码):https://github.com/almarklein/timetagger - 本专栏认知篇:《TimeTagger v26.1.3 介绍与选型指南》
- 本专栏部署篇:《TimeTagger v26.1.3 Docker Compose 部署指南》
- 本专栏 API 篇:《TimeTagger v26.1.3 API 使用详解》