@broberg/ 法学
现状:0.8.0。 这5份普通文件为v1.0,得到业主律师的批准(2/11 2026): draft: false,版本和“gælder fra' 2026-11-02。 第六个, modules (0.8.0)是2026年5月11日业主本人的文本决定,被故意不送交律师.
每个broberg.ai产品都有一套法律文件: terms, (中文(简体) ), privacy, (中文(简体) ), trust, (中文(简体) ), dpa 财务报告和审定财务报表
support。文本在任何地方都是一样的。 丹麦语是主人公,并且占上风;英语坐在旁边.
组件写入文本;每个产品填入 OWN 的事实并显示 OWN 上的文档
网站. legal.broberg.ai是一个API,不是网站:任何不属于API的东西都会发送访问者
给broberg.ai.
MUST:产品如何显示其法律文件
每一个带有可见服务的产品都显示这些文件,并且从不写自己的文件(所有者决定) D-7effa0 (英语). 这是每个新项目的固定入职步骤(TERMS/GDPR卡). 六个步骤:
1. 将你产品的事实写入你的遗书
文本有开放的空档(facts 每个产品的不同之处。 你给他们填满一个
ProductFacts 对象保存在您自己的 repo 中, 例如 legal/facts.ts数字 :
import type { ProductFacts } from "@broberg/legal";
export const FACTS: ProductFacts = {
id: "jam", // your product id: lower-case, stable
name: "Jam", // the name users know
hosting: [ // where data is stored and processed
{ what: "Appen", where: "Stockholm, Sverige (EU)" },
{ what: "Databasen", where: "Amsterdam, Holland (EU)" },
],
dataCategories: [ // what personal data you hold
"Navn, e-mail og profilbillede fra login",
"Dit indhold, som du selv gemmer",
],
subprocessors: [ // every vendor that touches personal data
{ name: "Fly.io", purpose: "Drift af app og database", location: "Sverige og Holland", transfer: "Databehandleraftale med EU-standardkontrakter" },
],
conversationAnalysis: true, // does the four-part conversation analysis apply?
healthData: false, // health data (GDPR art. 9)? physio apps: read docs/guides/health-data-physio.md
};
- 只列出今天的事实 如果你不确定,就别管了,问吧,别猜.
- 事实就是数据,从不改口. 不在文本中输入供应商名称或例外。 包裹 通过固定模板将您的事实变成表格和句子.
- 当您的产品改变时, 事实会发生变化( 一个新的供应商, 一个新的地区) 。 更新其中的文件 同样的变化.
- 通过测试来证明你的事实(建议;果酱这样做): 每个供应商在
subprocessors, (中文(简体) ), 需要代码中的证据,例如导入(/mistral/i),一个嵌入键(R2_ACCESS_KEY_ID)或(或)primary_region飞来飞去 那么事实不能保证你不用的卖家 并增加一个新的 整合失败测试,直到事实文件也这么说.
2. 获取文本:npm包(JS/TS建议)
import { getDocument, renderHtml } from "@broberg/legal"; // pin an exact version
import { FACTS } from "./legal/facts";
const doc = getDocument({ doc: "privacy", locale: "da", facts: FACTS });
doc.title; doc.version; doc.effectiveFrom; doc.draft; doc.contentHash;
renderHtml(doc); // semantic HTML with data-legal-id on every section and block; style it yourself
每个法律网页都包含什么(每日检查,F110.11): 包含文档的元素
已经 data-legal-doc="<terms|privacy|trust|dpa|support>" 和 data-legal-version="<version>",以及
每个段落都保持其 data-legal-id 原文为逐字记录. renderHtml 执行全部三个;如果您
自己做块,留着 风格和布局都是你的. scripts/check-legal-pages.mjs
在缺少的一页、缺失的一页标记和旧版本上获取页面并走红——命名您
或经编辑的段落.
短信飞船 内部 软件包,所以您的网站不需要网络,不能丢失 当法律上。 。 。 新文本作为新软件包版本到达您; 组件发送一个 说明措辞是否改变.
不需要调换新短信吗? (为运行中的服务器推荐;由jam端出)
import { createLegalCache } from "@broberg/legal";
const legal = createLegalCache({ facts: FACTS }); // once, at start
const doc = legal.get("privacy", "da"); // on every page view: never waits, never fails
get 立即从最新的好模板中解答,从包中捆绑的模板开始.
每个文件每天最多检查一次背景的API(If-None-Match) (中文(简体) ). 换个位置
一个更新的模板,但该模板必须核实和解决您的事实。 任何失败都会存在
旧的 缓存生活在内存中,所以从捆绑的文本中重新开始.
3. 或让他们通过HTTP(任何堆放)
GET https://legal.broberg.ai/v1/documents → { documents: [{ doc, locale, version, effectiveFrom, draft, hash }] }
GET https://legal.broberg.ai/v1/{doc}.{da|en}.json → the template: sections with facts slots open, + version, draft, stale, hash
在联署材料/TS中, resolveTemplate(template, FACTS) 从这个包裹。 单位
其他语言, 使每种语言 facts 这样块 : hosting 是一张桌子(在哪里);
dataCategories 是一个列表; subprocessors 是一个表格(Vendor QQ用于 QQ 转让);
conversationAnalysis 有一句话说,你的产品是否包含在
对话分析。 自0.6.0起,所有5个文件都有英文文本(locale: "en"),块用于
块与丹麦语相同——相同的ids,相同的顺序——所以一个链接或diff处理同一个段落
两种语言. stale: true 表示英文文本比丹麦文本老,所以显示
丹麦语或一种通知. 如果两者不同,丹麦文本优先(在文件中说明)
页:1. parityProblems(sources) 是让每个翻译都步入正轨的支票.
4. 快取: 保留自己的副本, 只有在有新版本( HTTP 路由) 时才获取新版本
你的语句页面绝对不能因为Legal.broberg.ai被删除或更新而下架.
- 在您的重播中保留一个快照或构建 (单位:千美元)
legal/snapshot/{doc}.{locale}.json,模板为 已获取). 您的页面渲染 从快照。 他们从未在页面视图中给API打电话. - 每天最多检查一次新版本 :
GET /v1/documents比较每个hash与 你的快照。 。 。 同样的大麻意味着没有什么可做的. - 只有当散列改变时: 获取模板并检查:
verifyTemplate(t), (中文(简体) ), 联 合 国hash必须在列表中平等。 然后在一步中替换快取文件( 写入为 临时文件,然后重命名). 陷阱 : 散列覆盖模板而不包含响应本身stale财务报告和审定财务报表hash字段。 整个反应都无法匹配 没有 软件包:将这两个字段放下,然后取出Canonic JSON中的sha256(按键排序,没有空格). - 如果有什么失败 (超时, 5xx, 检查失败), 保留旧快照, 登录它, 再试一次 明天见 从不空出一页, 因为获取失败 .
- 每个答案都包含着
ETag财务报告和审定财务报表cache-control: public, max-age=86400请求If-None-Match: "<hash>"答案为空304当一切都变了.
该服务运行冷:静静咒语后的第一个呼叫需要一两秒钟. 这样很好 每天检查,还有一个理由 永远不要在页面视图上调用它.
5. 在您的网站上显示文档
- 例如,每个文档在您自己的域上页
/vilkaar, (中文(简体) ),/privatliv, (中文(简体) ),/tillid。 。 。 .dpa财务报告和审定财务报表support合同经理人{customer.name}编辑 其他占位符); 公共网站显示terms, (中文(简体) ),privacy财务报告和审定财务报表trust。 。 。 . - 风格和回流自由. 永远不要编辑文本. 绘制块( E)
p, (中文(简体) ),note, (中文(简体) ),list, (中文(简体) ),table)自己设计,而不是使用renderHtml完全没事,只要 文本为逐字记录.renderHtml是一种便利,而不是要求. - 显示版本和“ gælder fra” 日期 (
version, (中文(简体) ),effectiveFrom) (中文(简体) ). 虽然draft是真的,显示 “UDKAST”标记renderHtml为了你. - 把他们都放到你的脚上. 永远不要将用户链接到 legal.broberg.ai它会重定向到broberg.ai.
6. 接受和同意:始终在投资发展局
每个登录的应用程序都会在BID中存储两个术语的接受和GDPR同意('Samtykke' /) “同意”银行帐户,没有其他办法 (所有者规则,1/11 2026;组件F110.6). 撤回同意 也通过BID. 永远不要保留自己的桌子.
-
术语 注册时接受 未连接 (原始内容存档于2018-10-21) (英语). Jeg accepter vilkårene' checkbox. app SERVER在BID中用用户的访问符记下:
POST /api/app/terms-acceptance { doc: "terms", version, contentHash: "sha256:<hex>" }它写着: 最新的一个GET /api/app/terms-acceptance?doc=terms。仅在当前条件时再次询问 为:material: true并且比用户的最新接受更新. -
隐私政策从未被接受. 它被链接和显示.
-
GDPR 同意书 (例如物理应用的第四部分分析) 是它自己的事情: 自愿的,永远不 使用条件, 并尽可能容易撤回和给予。 记录下来
POST /api/app/consents { purpose, granted: true|false, version, contentHash }。在使用之前 数据,读取当前状态GET /api/app/consents?purpose=…。用户可以退出 应用程序和他们的BID账户上, 在BID中提取的款项出现在你的下一张专辑上。 目的 来源于 BID 中的固定列表; 请求加入您的列表 . -
应用程序从您的登录客户端获取产品, 所以你只能触摸自己的用户和您的用户 拥有的产品.
-
以竞标方式注册您的文档页面 (例如
terms → https://jams.party/vilkaar当 你开始记录接受。 BID的“Samtykke”页面链接“Læs vilkårene”到该地址;a 应用程序从未发送 URL 本身 . -
线路是直播(BID 2/11 2026),这个包有呼叫 (服务器侧,与 用户访问令牌 :
import { getAcceptance, needsReaccept, recordAcceptance, recordConsent, getConsent, getTemplate } from "@broberg/legal"; const terms = getTemplate({ doc: "terms", locale: "da" }); const latest = await getAcceptance({ accessToken }, "terms"); if (needsReaccept(latest, terms)) { /* show the unticked checkbox; on accept: */ await recordAcceptance({ accessToken }, { doc: "terms", version: terms.version, contentHash: terms.hash }); } const consent = await getConsent({ accessToken }, "health-text-analysis"); // read BEFORE using the data await recordConsent({ accessToken }, { purpose: "health-text-analysis", granted: false, version, contentHash }); // withdraw每个记录调用返回存储的行 BID,并丢出
BidConsentError如果这行没有 跟你寄的一样 错误带有status财务报告和审定财务报表code(单位:千美元)invalid_token, (中文(简体) ),invalid_doc. . . . . . . “Samtykke”页面只显示用户接受的版本;他们是否必须再次接受是您的 应用程序的问题,由needsReaccept。如果账户是 删除(所有者2/11 2026).
参考文献
getDocument({ doc, locale, facts }): 来自捆绑模板的完整文档加上您的事实.getTemplate({ doc, locale }):模板,因为API为它服务.listDocuments(): 内容/v1/documents服务.resolveTemplate(template, facts): 从您获取的模板中获取的完整文档 。 它给 不变contentHash作为getDocument与同样的事实.renderHtml(document): 语义HTML.EXAMPLE_FACTS:一个填入的例子.createLegalCache({ facts, fetch?, refreshMs?, baseUrl?, onKeepOld? })→{ get(doc, locale), settled(doc, locale) }。 。 。 .verifyTemplate(t): 当一个被获取的模板有部分且其散列与其作者匹配时真实性 内容.LEGAL_API_URL:API的地址,在一个地方.- 内容Hash = 已完成文件的犬科JSON; 散列轴 = 模板相同.
- 占位符 (单位:千美元)
{customer.name}以物配主者,是固定的,故拒绝一个未知的. - 零依赖性(一个内置的SHA-256,针对FIPS向量和节点被证明:crypto).
0.5.0(F110.3): v1.0——所有者的律师审查并批准了所有5个文件. draft 现在 false 到处都是(没有“UDKAST”标记),以及 version编号 :effectiveFrom 移动到 2026-11-02措辞不变. 术语是: material这样 needsReaccept 答题 true 对接受草案(2026-11-01)的人: 要求他们接受一次批准的v1.0,这就是重点。 该版本故意保留一个ISO日期——它被比作字符串,“1.0”将在“2026-11-01”之前排序,并默地跳过重新接受.
0.4.0(F110.6): BID电话: recordAcceptance编号 :getAcceptance, (中文(简体) ), recordConsent编号 :getConsent, (中文(简体) ), needsReaccept, (中文(简体) ), BidConsentError, (中文(简体) ), BID_URL。 添加剂.
0.3.1(F110.16): (法语) 术语(da)“Dit indhold”不再保证“pseudnymiseret分析”每种产品。 产品分析是否只在隐私政策中说,每个产品(事实.conversationAnalysis). 术语文本及其散列变更(仍为草案). 被果酱找到的.
0.3.0 (F110.15): (法语) createLegalCache (调自果酱的F001.79), verifyTemplate, (中文(简体) ), LEGAL_API_URL· ;
README 第4节陷阱已固定 。 附加;正文不变.
0.2.0(F110.14),断: 车队产品登记簿和 library 不见了,而且
getDocument 带 facts 改为 product。每条产品HTTP路线和人类页面
也不见了:Legal.broberg.ai是一个API.
计划: docs/features/F110-legal-documents.md, (中文(简体) ), docs/features/F110.14-legal-is-an-api.md。 。 。 .
模块所有者和开发者的条件—— doc: "modules" (0.8.0,F110.17) (单位:千美元)
为诊所和开发商发布模块的平台(App Store). 短信说
平台对稳定性的答案,并可能就一个违反准则的模块采取行动:通知
警告,有最后期限 暂停 清除,以及 在严重案件中立即暂停 (数据:
危险、安全漏洞、不稳定、非法内容)——总是在一个人评估之后.
暂停期间保留数据,诊所不支付单元费用。 投诉,记录
每一步骤、付款和分配、重新接受实质性改变AcceptedDoc 包含
"modules"),丹麦法律.
文中无号. 每个数字都来自产品本身的事实 并出现在其中 表格(“Tal og frist”):
getDocument({ doc: "modules", locale: "da", facts: { ...myFacts, modules: {
revenueShare: [{ party: "platformen", percent: 30 }, { party: "modulejeren", percent: 70 }], // must add up to 100
operationsFee: "…", paymentProvider: "Mollie", payoutCadence: "månedligt",
guidelinesNoticeDays: 30, warningDeadlineDays: 14, guidelinesUrl: "https://…/retningslinjer",
complaints: "…", complaintsResponse: "3 hverdage", exportDays: 30,
} } });
没有产品 modules 事实得到错误,从没有空表的文档。 BID 必须
接受 "modules" 在接受记录之前作为文件类型(招标,5/11).
假名化—— @broberg/legal/pseudonymise (0.7.0, F110.8) (中文(简体) )
在输入6个月的分析副本之前,先通过这个进行每次对话。 技术和工艺
标准: docs/legal/pseudonymisation-standards.md. 匿名数据仍然是个人数据.
import { createConversation, toAnalysisRecord, deleteExpired } from "@broberg/legal/pseudonymise";
const key = process.env.ANALYSIS_PSEUDONYM_KEY!; // one domain, vault-held, never next to the copy
const convo = createConversation({ key, knownNames: [sender.name, ...parties] /*, findNames */ });
const rec = await toAnalysisRecord({ id, subjectId: user.id, text, collectedAt: new Date() }, convo, key);
// rec.subject = keyed pseudonym · rec.text = names → [PERSON-n], CPR → [CPR], e-mail/phone/IP → keyed markers,
// health sentences → [HELBRED FJERNET] · rec.keepUntil ≤ 6 months · rec.notExamined = what was NOT checked
await deleteExpired(store, new Date()); // your store: listExpired(now) + delete(ids)
- 同一个人,同一个假名 在一键下; 另一键 → 无关的假名 (旋转无链接).
- 名称 : 那些你通过
knownNames通过一次谈话,一致替换 (全名,单名,"梅特斯"). 你没有通过的名字没有找到,除非你 给findNames(例如通过欧盟模式)@broberg/ai-sdk)——并在此之前notExamined携带"names-not-known"测量:16/16 关于测试中的丹麦已知姓名. - 健康(第9条): 符合丹麦健康词汇的句子被删除,除非
health: "consented"(明示同意). 它是一个关键步骤:"health-beyond-keywords"这么说的. - 不检查地址和间接识别符(
notExamined) (中文(简体) ).