Databunker 0.8.37 API 使用详解

Databunker 0.8.37 REST API 全端点实测:用户记录、应用数据、会话、验证码登录、GDPR 合规工作流、审计与分享记录,含错误码速查与排障实录。

DatabunkerDatabunkerREST APIAPIGo隐私安全2026/11/03

Databunker 0.8.37 API 使用详解

Databunker 没有富 CLI,所有能力都收敛在一套 REST API 上——本文按域逐个实测:用户记录、应用数据、会话、验证码登录、同意与合规工作流、审计、分享记录、到期删除,并给出错误码速查与两个高频踩坑实录。所有请求与回显均为真实执行记录。

一、通用约定

项值
Base URLhttp://localhost:3000(官方默认;本文实验环境映射在 8134,下文命令以实测值为准)
认证头X-Bunker-Token: <令牌>
请求体JSON(Content-Type: application/json);也接受表单编码
响应统一 JSON,status 为 ok/error
安全响应头每个响应固定携带 CSP、HSTS、X-Content-Type-Options: nosniff、X-Frame-Options: SAMEORIGIN、XSS 防护头(见第八节实测头表)
CORS全放开:Access-Control-Allow-Origin: *,预检返回 204(第八节实测)

令牌注入方式(示例值均为本文实验自造,生产请按《Databunker 0.8.37 Docker Compose 部署指南》第四节重新生成):

# 根令牌来自初始化时的 env 文件
export ROOT=$(grep DATABUNKER_ROOTTOKEN .env/databunker-root.env | cut -d= -f2)
# 之后所有管理面请求: -H "X-Bunker-Token: $ROOT"

令牌种类:

种类来源作用域
根令牌(root)-init 时由 DATABUNKER_ROOTTOKEN 提供全部管理面 API
临时令牌(xtoken)验证码登录流程换取仅本人记录(实测越权访问返回 403)
用户 token创建记录返回的 UUID标识记录用,不是凭据

健康检查:GET /v1/status 返回纯文本 OK(无需令牌,镜像内置探活即用此端点)。

二、用户记录 CRUD(核心域)

2.1 创建

curl -s -X POST http://localhost:8134/v1/user \
  -H "X-Bunker-Token: $ROOT" -H 'Content-Type: application/json' \
  -d '{"first":"John","last":"Doe","login":"api.d5","email":"api.d5@example.com","phone":"+441234567891","password":"q1w2e3r4","custom":"d5vip"}'
{"status":"ok","token":"da58bc19-ce07-07b0-be2f-cc0ebb6b9c19"}

字段完全自由(任意 JSON 键均可入库),但 4 个键参与唯一索引:login、email、phone、custom。重复值直接拒绝:

curl -s -X POST http://localhost:8134/v1/user -H "X-Bunker-Token: $ROOT" \
  -H 'Content-Type: application/json' -d '{"login":"api.d5b","custom":"d5vip"}'
{"status":"error","message":"duplicate index: custom"}
curl -s -X POST http://localhost:8134/v1/user -H "X-Bunker-Token: $ROOT" \
  -H 'Content-Type: application/json' -d '{"email":"api.d5@example.com"}'
{"status":"error","message":"duplicate index: email"}

custom 的语义是"业务自定义唯一引用"(如内部客户号),实测与 login/email/phone 同级去重。

匿名创建:官方默认配置 create_user_without_access_token: true,不带任何令牌也能建档(面向注册场景):

curl -s -X POST http://localhost:8134/v1/user -H 'Content-Type: application/json' -d '{"email":"anon.d5@example.com"}'
{"status":"ok","token":"97406150-112f-902e-e9e2-0d72019e9521"}

内部系统请在 YAML 关闭该开关(见《Databunker 0.8.37 配置参数详解》5.1 节)。

2.2 检索:五种定位方式

GET /v1/user/:mode/:identity,mode ∈ token | login | email | phone | custom:

curl -s -H "X-Bunker-Token: $ROOT" http://localhost:8134/v1/user/login/api.d5
{"status":"ok","token":"da58bc19-ce07-07b0-be2f-cc0ebb6b9c19","data":{"custom":"d5vip","email":"api.d5@example.com","first":"John","last":"Doe","login":"api.d5","password":"q1w2e3r4","phone":"+441234567891"}}

按 email/token 检索回显相同;电话检索需归一化——入库 +441234567891,查询用国际格式去加号补零:

curl -s -H "X-Bunker-Token: $ROOT" http://localhost:8134/v1/user/phone/00441234567891
{"status":"ok","token":"da58bc19-ce07-07b0-be2f-cc0ebb6b9c19","data":{...同上...}}

非法 mode 直接拒绝:

curl -s -i -H "X-Bunker-Token: $ROOT" http://localhost:8134/v1/user/invalid/api.d5 | sed -n '1p;$p'
HTTP/1.1 405 Method Not Allowed
{"status":"error","message":"bad mode"}

2.3 修改(PUT,增量合并)

curl -s -X PUT http://localhost:8134/v1/user/token/da58bc19-ce07-07b0-be2f-cc0ebb6b9c19 \
  -H "X-Bunker-Token: $ROOT" -H 'Content-Type: application/json' \
  -d '{"first":"Johnny","custom":"gold5"}'
{"status":"ok","token":"da58bc19-ce07-07b0-be2f-cc0ebb6b9c19"}

回读确认只动了传入字段(last/email 等保持原值):

curl -s -H "X-Bunker-Token: $ROOT" http://localhost:8134/v1/user/token/da58bc19-ce07-07b0-be2f-cc0ebb6b9c19
{"status":"ok","token":"da58bc19-ce07-07b0-be2f-cc0ebb6b9c19","data":{"custom":"gold5","email":"api.d5@example.com","first":"Johnny","last":"Doe","login":"api.d5","password":"q1w2e3r4","phone":"+441234567891"}}

2.4 删除(被遗忘权)

curl -s -X DELETE http://localhost:8134/v1/user/token/da58bc19-ce07-07b0-be2f-cc0ebb6b9c19 \
  -H "X-Bunker-Token: $ROOT"
{"status":"ok","result":"done"}

删除后按 token 仍可读,但 data 为空对象(记录内容被加密擦除,token 行保留占位):

curl -s -H "X-Bunker-Token: $ROOT" http://localhost:8134/v1/user/token/da58bc19-ce07-07b0-be2f-cc0ebb6b9c19
{"status":"ok","token":"da58bc19-ce07-07b0-be2f-cc0ebb6b9c19","data":{}}

级联效果:该用户的 userapp 数据同步不可读:

curl -s "http://localhost:8134/v1/userapp/token/da58bc19-ce07-07b0-be2f-cc0ebb6b9c19/webshop" -H "X-Bunker-Token: $ROOT"
{"status":"error","message":"not found"}

自助删除(forget me):selfservice.forget_me: true 时,用户凭 xtoken 即可 DELETE /v1/user/email/<自己>,实测返回 {"status":"ok","result":"done"}。

2.5 批量列表(默认关闭)

curl -s -X POST http://localhost:8134/v1/users -H "X-Bunker-Token: $ROOT" -H 'Content-Type: application/json' -d '{}'

默认返回 Access denied(HTTP 403)。开启 generic.list_users: true 后返回全量列表,注意 private 字段是 base64 编码明文(编码≠加密),只应作为受控导出通道:

{"status":"ok","total":3,"rows":[{"token":"...","private":"eyJlbWFpbCI6...","active":true},...]}

三、应用数据域 userapps

一条用户记录可挂多个命名应用域(如 webshop 存收货地址),与主记录独立读写:

# 创建
curl -s -X POST "http://localhost:8134/v1/userapp/token/da58bc19-.../webshop" \
  -H "X-Bunker-Token: $ROOT" -H 'Content-Type: application/json' \
  -d '{"address":"Baker Street 221B","city":"London","country":"UK"}'
{"status":"ok","token":"da58bc19-ce07-07b0-be2f-cc0ebb6b9c19"}
# 读取
curl -s "http://localhost:8134/v1/userapp/token/da58bc19-.../webshop" -H "X-Bunker-Token: $ROOT"
{"status":"ok","token":"da58bc19-ce07-07b0-be2f-cc0ebb6b9c19","data":{"address":"Baker Street 221B","city":"London","country":"UK"}}

PUT 同为增量合并(只传 city 即只改 city):

{"status":"ok","token":"da58bc19-ce07-07b0-be2f-cc0ebb6b9c19"}
# 读回:city 已变,address/country 保留
curl -s "http://localhost:8134/v1/userapp/token/da58bc19-.../webshop" -H "X-Bunker-Token: $ROOT"
{"status":"ok","token":"da58bc19-ce07-07b0-be2f-cc0ebb6b9c19","data":{"address":"Baker Street 221B","city":"Manchester","country":"UK"}}
# 用户的应用域列表 / 全库应用名清单
curl -s "http://localhost:8134/v1/userapps/token/da58bc19-..." -H "X-Bunker-Token: $ROOT"
curl -s http://localhost:8134/v1/userapps -H "X-Bunker-Token: $ROOT"
{"status":"ok","total":1,"rows":[...]}
{"status":"ok","apps":["webshop"]}

主记录检索默认不带应用域数据(GET /v1/user/... 只返回主 data),应用域须显式按名读取。

四、会话 sessions

记录用户设备/会话上下文(IP、UA 等),供审计与风控:

curl -s -X POST "http://localhost:8134/v1/session/108c8ce2-d98b-457c-99dc-3ed688f6f101" \
  -H "X-Bunker-Token: $ROOT" -H 'Content-Type: application/json' \
  -d '{"login":"api.d5","data":{"ip":"10.0.0.9","agent":"curl"}}'
{"status":"ok","session":"108c8ce2-d98b-457c-99dc-3ed688f6f101"}

读取、列表、删除:

curl -s "http://localhost:8134/v1/session/108c8ce2-..." -H "X-Bunker-Token: $ROOT"
{"status":"ok","session":"108c8ce2-d98b-457c-99dc-3ed688f6f101","when":1793677985,"data":{"data":{"agent":"curl","ip":"10.0.0.9"},"login":"api.d5"}}
curl -s "http://localhost:8134/v1/sessions/token/da58bc19-..." -H "X-Bunker-Token: $ROOT"
{"status":"ok","total":1,"rows":[{"when":1793677985,"session":"108c8ce2-d98b-457c-99dc-3ed688f6f101","data":{"data":{"agent":"curl","ip":"10.0.0.9"},"login":"api.d5"}}]}
curl -s -X DELETE "http://localhost:8134/v1/session/108c8ce2-..." -H "X-Bunker-Token: $ROOT"
{"status":"ok"}

⚠️ 会话与用户的关联靠请求体顶层的 token/login/email/phone 字段定位用户,不是 user_ref——踩坑过程见第九节实录 1。会话另有 policy.max_session_retention_period(默认 1h)自动清理。

五、验证码登录流程(换取 xtoken)

Web 门户/API 共用的登录链路,四步(实测端到端):

5.1 获取验证码串

GET /(门户首页 HTML)内嵌 var captchaCode = "<hex串>"——服务端用主密钥派生的 AES-GCM 密钥加密的"随机6位数字:时间戳",有效期 120 秒:

captcha 串(节选): cf473b17465299f2d41890fe44f5b4c49789604b ...

配套 GET /v1/captcha/:code 返回同数字的 PNG 图(人机校验展示用)。

5.2 prelogin

用户提交身份 + 验证码数字 + captcha 串:

curl -s "http://localhost:8134/v1/prelogin/email/test@securitybunker.io/538072/<captcha串>"
{"status":"ok","result":"done"}

服务端校验 captcha 解密值与提交数字一致后,把一次性登录码发给用户邮箱(SMTP 段配置);演示账号(email=test@securitybunker.io 或 phone=4444)走固定登录码 4444,无需邮件网关。

5.3 login

curl -s "http://localhost:8134/v1/login/email/test@securitybunker.io/4444"
{"status":"ok","xtoken":"b6ce922e-82c4-0b74-d00d-fcdc9ae0648d","token":"377042ed-f0c0-20b0-fb46-c1a0ecf32638"}

xtoken 即该用户的临时访问令牌(存 xtokens 表,受 max_user_retention_period 约束)。

5.4 xtoken 作用域实测

本人可读:

curl -s -H "X-Bunker-Token: b6ce922e-82c4-0b74-d00d-fcdc9ae0648d" \
  http://localhost:8134/v1/user/email/test@securitybunker.io
{"status":"ok","token":"377042ed-f0c0-20b0-fb46-c1a0ecf32638","data":{"email":"test@securitybunker.io","first":"Demo","last":"User","phone":"4444"}}

他人记录被拒:

curl -s -i -H "X-Bunker-Token: b6ce922e-82c4-0b74-d00d-fcdc9ae0648d" \
  http://localhost:8134/v1/user/login/api.d5 | sed -n '1p;$p'
HTTP/1.1 403 Forbidden
Access denied

六、GDPR 合规域

6.1 法律依据与同意

# 内置法律依据清单(-init 时自动导入 2 条登录相关 consent)
curl -s http://localhost:8134/v1/lbasis -H "X-Bunker-Token: $ROOT"
{"status":"ok","total":2,"rows":[{"basistype":"consent","brief":"core-send-email-on-login","creationtime":1793677208,"fulldesc":"Confirm to allow sending access code using 3rd party email gateway","module":"login","requiredflag":true,"requiredmsg":"This consent is required to give you our service.","shortdesc":"Send email on login","status":"active","usercontrol":true},{"basistype":"consent","brief":"core-send-sms-on-login","creationtime":1793677208,"fulldesc":"Confirm to allow sending access code using 3rd party SMS gateway","module":"login","requiredflag":true,"requiredmsg":"This consent is required to give you our service.","shortdesc":"Send SMS on login","status":"active","usercontrol":true}]}

接受/撤回同意(POST/DELETE /v1/agreement/:brief/:mode/:identity):

curl -s -X POST "http://localhost:8134/v1/agreement/core-send-email-on-login/email/test@securitybunker.io" \
  -H "X-Bunker-Token: $ROOT"
{"status":"ok"}
curl -s "http://localhost:8134/v1/agreements/email/test@securitybunker.io" -H "X-Bunker-Token: $ROOT"
{"status":"ok","total":1,"rows":[{"agreementmethod":"api","brief":"core-send-email-on-login","creationtime":1793677996,"endtime":0,"lastmodifiedby":"","mode":"email","starttime":0,"status":"yes","token":"377042ed-f0c0-20b0-fb46-c1a0ecf32638","when":1793677996,"who":"test@securitybunker.io"}]}

brief 不存在时返回 {"status":"error","message":"not found"}——先查 /v1/lbasis 拿合法 brief。

6.2 处理活动(ROPA)

curl -s -X POST http://localhost:8134/v1/pactivity/marketing -H "X-Bunker-Token: $ROOT" \
  -H 'Content-Type: application/json' -d '{"name":"Newsletter","purpose":"email marketing","thirdcountry":false}'
{"status":"ok"}
curl -s http://localhost:8134/v1/pactivity -H "X-Bunker-Token: $ROOT"
{"status":"ok","total":1,"rows":[{"activity":"marketing","applicableto":"","briefs":null,"creationtime":1793677997,"fulldesc":"","script":"","title":"marketing"}]}

POST /v1/pactivity/:activity/:brief 可把处理活动与法律依据挂接。

6.3 到期删除(exp)

给用户记录启动"到期删除倒计时",返回 exptoken 供用户点击确认/取消(邮件链接场景):

curl -s -X POST "http://localhost:8134/v1/exp/start/login/api.d5" -H "X-Bunker-Token: $ROOT"
{"status":"ok","exptoken":"5e307433-50f2-ada2-ed9a-f667468b3006"}
curl -s "http://localhost:8134/v1/exp/status/login/api.d5" -H "X-Bunker-Token: $ROOT"
{"status":"ok","exptime":1801713197,"expstatus":"wait","exptoken":"5e307433-50f2-ada2-ed9a-f667468b3006"}
curl -s -X DELETE "http://localhost:8134/v1/exp/cancel/login/api.d5" -H "X-Bunker-Token: $ROOT"
{"status":"ok"}

expstatus 为 wait(倒计时中);到期后由清理任务物理删除。另有 GET /v1/exp/retain/:exptoken(用户挽留)与 GET /v1/exp/delete/:exptoken(立即删除)两个免令牌确认端点。

6.4 可分享记录

生成匿名分享链接(可限定字段与存活时间):

curl -s -X POST "http://localhost:8134/v1/sharedrecord/login/api.d5" -H "X-Bunker-Token: $ROOT" \
  -H 'Content-Type: application/json' -d '{"fields":"first,last","ttl":60}'
{"status":"ok","record":"95e5e7b0-2d18-81b8-fae3-4668a16f3cc8"}

任何人(无令牌)按 record id 读取被授权字段:

curl -s http://localhost:8134/v1/get/95e5e7b0-2d18-81b8-fae3-4668a16f3cc8
{"status":"ok","data":{"first":"Johnny","last":"Doe"}}

6.5 DPO 审批工作流

GET /v1/requests(root)与 GET /v1/requests/:mode/:identity 列出用户发起、待审批的 GDPR 请求(删除/变更申请经 Web 门户提交,API 侧提供查询/批准 POST /v1/request/:request/取消 DELETE)。空队列回显:

{"status":"ok","total":0,"rows":[]}

6.6 审计留痕

每次 API 操作自动写入 audit 表(可用 disable_audit 关闭;保留期 max_audit_retention_period 默认 6 个月):

curl -s "http://localhost:8134/v1/audit/list/da58bc19-..." -H "X-Bunker-Token: $ROOT" | head -c 900
{"status":"ok","total":21,"rows":[{"atoken":"b44f71d4-604b-95d7-f86d-b84e708f32fb","identity":"root","mode":"login","more":false,"record":"da58bc19-ce07-07b0-be2f-cc0ebb6b9c19","status":"ok","title":"initiate user record expiration by login","when":1793677997,"who":"api.d5"},{"atoken":"637b83a1-130d-a6df-3717-6ad3616d50f4","identity":"root","mode":"token","more":false,"msg":"generated: 95e5e7b0-2d18-81b8-fae3-4668a16f3cc8","record":"da58bc19-ce07-07b0-be2f-cc0ebb6b9c19","status":"ok","title":"create shareable record by login","when":1793677997,"who":"api.d5"},{"atoken":"f6330382-df37-10b8-77d4-7e5ff65a339c","identity":"root","mode":"login","more":false,"record":"da58bc19-ce07-07b0-be2f-cc0ebb6b9c19","status":"ok","title":"get expiration status by login","when":1793677997,"who":"api.d5"},...]}

每条含 who(身份标识)、identity(令牌角色)、title(操作描述)、record(受影响记录)、when(秒级时间戳)。GET /v1/audit/admin 为全局视图(root 令牌),GET /v1/audit/get/:atoken 按事件取详情。

七、运维端点

  • Prometheus 指标:GET /v1/metrics(root 令牌),标准 Go 运行时指标 + HTTP 直方图:
# HELP go_gc_duration_seconds A summary of the wall-time pause (stop-the-world) duration in garbage collection cycles.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 8.9462e-05
go_gc_duration_seconds{quantile="0.25"} 0.000105444
go_gc_duration_seconds{quantile="0.5"} 0.000156904
  • 备份:GET /v1/sys/backup(root 令牌)。实测 PostgreSQL 后端返回 HTTP 200, 0 bytes——该端点仅 SQLite 后端有内容,PostgreSQL/MySQL 备份请用 pg_dump(见部署指南第十一节)。
  • 批量 API(/v1/bulk/*):默认未挂载,请求返回 {"status":"error", "message":"endpoint is missing"}(HTTP 404)。
  • 版本:/databunker/bin/databunker -version → Databunker version: 0.8.37-20261030100830-7667c5056897。

八、CORS 与安全响应头

浏览器直连场景实测预检:

curl -s -i -X OPTIONS http://localhost:8134/v1/user \
  -H 'Origin: http://example.com' -H 'Access-Control-Request-Method: POST' \
  -H 'Access-Control-Request-Headers: X-Bunker-Token'
HTTP/1.1 204 No Content
Access-Control-Allow-Headers: Accept,  Content-Type, Content-Length, Accept-Encoding, X-Bunker-Token
Access-Control-Allow-Methods: POST, PUT, DELETE
Access-Control-Allow-Origin: *
Allow: OPTIONS, POST
Content-Security-Policy: default-src 'self' http: https: data: blob: 'unsafe-inline'
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block

CORS 为源码硬编码全放开(Allow-Origin: *),无配置项——前端直连时务必自行评估令牌暴露面(浏览器里放 root 令牌等于裸奔,前端场景只应使用登录换取的 xtoken)。

九、错误码速查与排障实录

9.1 错误码速查(全部实测)

场景HTTP响应体
记录不存在405{"status":"error","message":"not found"}
mode 非法(非 token/login/email/phone/custom)405{"status":"error","message":"bad mode"}
令牌错误/越权403Access denied(纯文本)
唯一索引冲突200{"status":"error","message":"duplicate index: <字段>"}
端点不存在/未启用404{"status":"error", "message":"endpoint is missing"}
成功写操作200{"status":"ok","result":"done"} 或带 token

注意两处反直觉语义:"记录不存在"用 405 而非 404(源码 ReturnError(..., 405, ...) 统一处理),唯一键冲突 HTTP 仍是 200,调用方必须检查 status 字段。

实录 1:session 创建"成功"却查不到

  • 现象:按直觉把用户令牌放在 user_ref 字段创建会话,返回 {"status":"ok","session":"94d992f3-..."},但 GET /v1/sessions/token/<用户> 恒为 total:0。
  • 根因:sessionCreate 只识别请求体顶层的 token/login/email/phone 来解析用户(源码 users_api.go 的 checkRecord()),user_ref 被当作普通业务字段存入密文,用户关联为空。
  • 解法:-d '{"login":"api.d5","data":{...}}'。改用正确姿势后同一查询返回 total:1(见第四节回显)。

实录 2:健康检查探错端点

  • 现象:按惯例配 GET /health 探活,返回 404 endpoint is missing。
  • 根因:Databunker 健康检查端点是 /v1/status 或 /status(镜像内置 HEALTHCHECK 即用它),没有 /health。
  • 解法:探活配置指向 /v1/status,期望 200 + OK。

十、集成建议

  1. 主库只存 token:业务表加 user_token 列引用 Databunker,PII 不落业务库;检索用 login/email/phone 模式,无需本地索引。
  2. 令牌分层:服务端持 root 令牌;浏览器/客户端只允许登录换取的 xtoken(403 实测保证越权不可行)。
  3. 写操作判 status:唯一键冲突返回 200,务必解析 JSON 而非仅看状态码。
  4. 合规三件套尽早启用:audit(默认开)、notification_url(事件外推)、policy 保留期(默认 3m/6m/1h/1m),对应 GDPR 审计、泄露通知、存储限制义务。
  5. 官方客户端:Node.js @databunker/store(npm install @databunker/store,用法见官方 doc/nodejs 页)与 Python 示例(GitHub python 目录)封装了本节全部端点。