Documentation
IntentD 接入与数据格式
面向扩展开发者和买方数据工程师的技术规范。开始发送事件或读取导出所需的全部内容。
Manifest V3 HMAC-SHA256 X25519 + AES-GCM Parquet + LZ4 PII-free
01 · Publishers
SDK Quickstart
SDK 运行在扩展的 background script 中,不需要访问页面 DOM。打包文件在控制台中按扩展单独生成:密钥、加密密钥和扩展 ID 都已内置,无需手动调用 init。在用户同意之前,SDK 不采集任何数据 —— 同意流程是如何设计的.
# 控制台 → 扩展 → SDK → 下载
# intentd-<name>.min.js 文件已包含您的密钥和配置。
cp ~/Downloads/intentd-my-extension.min.js ./extension/intentd.min.js // background.js —— 第一行,顶层作用域。
// init 会自动调用;监听器必须在 service worker 启动时注册,
// 否则 Manifest V3 不会投递事件。
import './intentd.min.js';
// 可选:查询状态与手动发送。
// const status = await IntentD.status();
// await IntentD.flush(); 对 manifest 的要求
-
manifest_version: 3—— SDK 不使用 eval,也不从外部加载代码。 -
storage,alarms,unlimitedStorage权限:事件在本地累积,每天打包发送一次。 -
webNavigation(或 tabs)—— 导航事件的来源。 -
api.intentd.io写入 host_permissions。
下载之前,请在控制台填写商店中的扩展 ID:打包文件会拒绝在任何其他扩展中运行,并给出提示。
02 · Publishers
Ingest API
- Base URL
- https://api.intentd.io/v1
- 身份验证
- X-API-Key + 对请求体的 HMAC-SHA256
接口
| 方法 | 路径 | 用途 | 限额 |
|---|---|---|---|
| POST | /sdk/register | 注册安装实例:会话和数据包标识符。幂等。 | 每个 IP 120 次请求 / 分钟 |
| POST | /telemetry/bucket | 加密的事件数据包(X25519 + AES-256-GCM)。每天一次,或提前发送。 | 每个安装 48 个数据包 / 天 |
| POST | /sdk/report-tamper | SDK 上报完整性校验失败。 | 每个 IP 30 次请求 / 分钟 |
请求示例
POST /v1/telemetry/bucket HTTP/1.1
Host: api.intentd.io
Content-Type: application/json
X-API-Key: ext_...
X-Timestamp: 1758000000
# hex(HMAC-SHA256(secret, "intentd-v2|bucket|<X-Timestamp>|<X-API-Key>|" + hex(SHA-256(body))))
X-Signature: d49d3b20...
{
"bucket_id": "7c0e…", "session_id": "2b91…",
"ephemeral_public_key": "base64(32 字节)",
"iv": "base64(12 字节)",
"encrypted_key": "base64(48 字节)",
"ciphertext": "base64(INTD 数据包的 AES-256-GCM 密文)",
"events_count": 1234, "bytes_size": 45678, "bucket_version": 1,
"client_ts": 1758000000, "fingerprint_hash": "5c9a96…"
} 错误码
| HTTP | 代码 | 含义 |
|---|---|---|
| 400 | BUCKET_INVALID_FORMAT | 数据包解密失败,或未通过格式与 CRC32 校验 |
| 401 | API_KEY_REVOKED | 密钥已吊销 —— SDK 自行停用 |
| 401 | INVALID_SIGNATURE | HMAC 签名与请求体不匹配 |
| 401 | CLOCK_SKEW | 客户端时钟偏差超过 5 分钟;响应中带有 server_time |
| 402 | SUBSCRIPTION_INACTIVE | 没有有效订阅 —— 事件留在队列中 |
| 403 | WRONG_EXTENSION | 打包文件运行在并非为其签发的扩展中 |
| 403 | FINGERPRINT_MISMATCH | 运行环境与会话不符 —— SDK 重新注册 |
| 409 | BUCKET_ALREADY_RECEIVED | 该数据包已被接收 |
| 413 | BUCKET_TOO_LARGE | 超过 10,000 条事件或 10 MB —— SDK 会拆分数据包 |
| 429 | RATE_LIMITED | 超出请求频率或每日数据包限额 |
遇到网络错误、429 和 503 时,SDK 会保留队列并每小时重试一次。二进制数据包格式和加密方案已公开 —— INTD v1 规范.
03 · Data Buyers
数据结构
一行 Parquet 就是一条清洗后的事件。结构中按设计不含任何个人数据字段:它们在浏览器里、发送之前就被丢弃了。
| 字段 | 类型 | 说明 |
|---|---|---|
| event_id | String | 事件的唯一 GUID |
| timestamp | Int64 | Unix 时间戳,毫秒,UTC |
| anon_uid | String | 用户会话的 HMAC 哈希,不可逆 |
| clean_url | String | 剥离 PII 参数后的 URL |
| search_query | String | 提取出的搜索意图(如果有) |
| lang | String | 浏览器语言,BCP 47 |
| geo_country | String | ISO 国家代码,在 edge 层按 IP 解析 growth+ |
| device_type | String | desktop / mobile / tablet growth+ |
| domain | String | 不含子域名的根域名 |
Data Buyers
Data Provenance
- 所有事件都只来自在同意提示中明确点击「允许」的用户(GDPR 第 6(1)(a) 条)。在同意之前,SDK 不创建标识符,也不存储事件。
- 每个被接收的数据包都携带同意文本的版本号;服务器会拒绝不带版本号的数据包,并在接收日志中记录该版本。文本变更后会重新征求同意。
- 用户撤回同意后,SDK 停止采集并删除设备上未发送的事件;下一次授予同意会生成一个与此前无关的新假名。
- 服务器不存储某位用户是否同意以及何时同意:证据是被接收的数据包本身和其中记录的文本版本。
04 · Data Buyers
存储与交付
- 格式
- Parquet + LZ4
- 存储
- AWS S3
- 热存储访问
- 30 天
- 归档
- 90 天
路径结构
year=YYYY/month=MM/day=DD/tenant=<key_id>__<bucket_id>.parquet.lz4 按日期和租户分区,使您无需全量扫描 bucket 就能读取所需切片。Growth 及以上套餐还支持同步到您自己的 bucket。
05 · Data Buyers
读取示例
导出文件可用任意标准 Parquet 技术栈读取。下面是 Python 和 Go 的最小示例。
import pandas as pd
# S3 中某个租户的日分区。
path = "s3://intentd-delivery/year=2026/month=09/day=11/tenant=ext_acme__7c0e.parquet.lz4"
frame = pd.read_parquet(
path,
columns=["timestamp", "domain", "search_query", "geo_country"],
storage_options={
"key": "YOUR_ACCESS_KEY",
"secret": "YOUR_SECRET_KEY",
},
)
top_domains = frame.groupby("domain").size().sort_values(ascending=False).head(20)
print(top_domains) package main
import (
"context"
"log"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3"
)
func main() {
ctx := context.Background()
cfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
log.Fatal(err)
}
client := s3.NewFromConfig(cfg)
object, err := client.GetObject(ctx, &s3.GetObjectInput{
Bucket: ptr("intentd-delivery"),
Key: ptr("year=2026/month=09/day=11/tenant=ext_acme__7c0e.parquet.lz4"),
})
if err != nil {
log.Fatal(err)
}
defer object.Body.Close()
// 之后用任意 Parquet 读取库即可,例如 parquet-go。
log.Printf("已接收 %d 字节", *object.ContentLength)
}
func ptr(s string) *string { return &s } 准备好接入了吗?
创建账户即可获取密钥并预留数据导出。如果需要在签约前看样本数据,请联系我们。
门户的完整 OpenAPI 规范位于仓库中: portal/docs/swagger.yaml. 公开托管版本将随控制台一起推出 —— 告诉我们您需要它.