diff --git a/.github/scripts/check-positioning.mjs b/.github/scripts/check-positioning.mjs index 3ddefc6..3017654 100644 --- a/.github/scripts/check-positioning.mjs +++ b/.github/scripts/check-positioning.mjs @@ -11,7 +11,8 @@ * bodies. It reads the build, not the sources: `lib/i18n.ts` keeps * "ObjectStack Documentation" in a comment that ships nowhere, as #171 allowed. * (c) The stale sentences #171 removed stay out of the English `content/docs/` - * sources, outside code fences and MDX comments. + * sources, outside code fences and MDX comments, and out of the `en` entry + * of the two legal pages (`terms`, `privacy`), whose licence facts PR 2 fixed. * * Run it after `pnpm turbo run build`; a missing build fails. `--self-test` runs the fixtures. */ @@ -25,6 +26,7 @@ const INDEX = 'content/docs/index.mdx'; const GLOSSARY = 'content/docs/resources/glossary.mdx'; const BUILD = 'apps/docs/.next/server/app'; const LOCALE_SIBLING = /\.[a-z]{2}(?:-[A-Z][a-z]{3})?\.mdx$/; +const LEGAL = ['apps/docs/app/[lang]/terms/page.tsx', 'apps/docs/app/[lang]/privacy/page.tsx']; // (c) reads their `en` entry const MISSPELT = /\b(?:ObjectStack Protocol|ObjectStack Documentation|Object OS|objectOS|ObjectOs)\b/; const STALE = [ @@ -35,6 +37,9 @@ const STALE = [ /No\s+licen[cs]e\s+server/, // capital N: "no seats, ..., no license server" is the open runtime and stays /[Ff]ully\s+self-contained/, /[Ii]nside\s+your\s+firewall/, + /[Dd]oes\s+not\s+phone\s+home/, + /ObjectOS\s+is\s+distributed\s+under\s+the\s+Apache/, // ObjectOS only: the open ObjectStack runtime is, and may say so + /[Cc]ustomer-hosted\s+runtime/, ]; const OPEN_SOURCE = /Open[\s-]+source,\s+Apache-2\.0/i; // checked inside the glossary's ObjectOS entry only @@ -90,11 +95,18 @@ const ruleB = (files) => return m ? [`(b) ${path} spells the brand ${JSON.stringify(m[0])}; it is ObjectOS`] : []; }); +/** A legal page's `en:` entry with the rest blanked so a finding's line holds; undefined when it has none, which (c) reports. */ +function englishEntry(tsx) { + const m = /^ en: \{[\s\S]*?^ \},/m.exec(tsx ?? ''); + return m ? tsx.slice(0, m.index).replace(/[^\n]/g, ' ') + m[0] + tsx.slice(m.index + m[0].length).replace(/[^\n]/g, ' ') : undefined; +} + /** (c) No English source brings a stale sentence back. Fences and comments are blanked, lines kept. */ function ruleC(files) { const out = []; const blank = (m) => m.replace(/[^\n]/g, ' '); for (const [path, text] of files) { + if (text === undefined) { out.push(`(c) ${path} has no English text this gate can read, so it was not measured`); continue; } const prose = text.replace(/\{\/\*[\s\S]*?\*\/\}|^ {0,3}(`{3,}|~{3,})[\s\S]*?^ {0,3}\1/gm, blank); for (const re of STALE) { const m = re.exec(prose); @@ -123,14 +135,14 @@ function gate() { const findings = [ ...ruleA({ ts: read(CONSTANT), index: read(INDEX), notFound: read(`${BUILD}/_not-found.html`), llms: read(`${BUILD}/llms.txt.body`) }), ...ruleB(shipped.map((p) => [p, read(p)])), - ...ruleC(sources.map((p) => [p, read(p)])), + ...ruleC([...sources.map((p) => [p, read(p)]), ...LEGAL.map((p) => [p, englishEntry(read(p))])]), ]; for (const f of findings) console.error(` ${f}`); if (findings.length) { console.error(`\n✗ positioning: ${findings.length} finding(s). The rules are in this script's header.`); return 1; } - console.log(`✓ positioning: 3 copies equal the constant; the brand is right in ${pages.length} pages and 2 llms bodies; no stale sentence in ${sources.length} English sources`); + console.log(`✓ positioning: 3 copies equal the constant; the brand is right in ${pages.length} pages and 2 llms bodies; no stale sentence in ${sources.length} English sources and the en entry of ${LEGAL.length} legal pages`); return 0; } @@ -149,6 +161,8 @@ const A_BAD = { const B_BAD = ['ObjectStack Protocol', 'ObjectStack Documentation', 'Object OS', 'objectOS', 'ObjectOs']; const C_OK = 'No seats, no license server. Does ObjectOS phone home?\n\n```\nObjectOS is a self-hosted runtime\n```\n\n{/* was: never phones home */}\n'; const C_BAD = 'ObjectOS is a self-hosted\nruntime. It never phones home, does not call home, never calls home.\n\nNo license server. Fully\nself-contained, inside your firewall.\n'; +const LEGAL_OK = "const content = {\n en: {\n text: 'ObjectOS Cloud is hosted. The open-source ObjectStack runtime has no telemetry, no license check, and no update ping.',\n },\n 'zh-Hans': {\n text: 'customer-hosted runtime; does not phone home',\n },\n};\n"; +const LEGAL_BAD = " en: {\n text: 'ObjectOS is distributed under the Apache License 2.0. ObjectOS is a customer-hosted\nruntime that does not phone home.',\n },\n"; const glossary = (entry) => [GLOSSARY, `### ObjectOS\n\n${entry}\n\n### ObjectStack\n\nOpen source, Apache-2.0.\n`]; const CASES = [ ['(a) the three copies equal the constant', () => ruleA(A_OK), 0], @@ -157,6 +171,8 @@ const CASES = [ ['(b) the five wrong spellings, one file each', () => ruleB(B_BAD.map((s, i) => [`${i}.html`, `
${s} boots.
`])), 5], ['(c) list form, a question, a fenced quote, a comment', () => ruleC([glossary('Commercial. Not open source.'), ['a.mdx', C_OK]]), 0], ['(c) the eight stale sentences, three wrapped', () => ruleC([glossary('The runtime. Open\nsource, Apache-2.0.'), ['a.mdx', C_BAD]]), 8], + ['(c) a legal page: a true en entry; its stale zh-Hans entry is not read', () => ruleC([glossary('Commercial.'), [LEGAL[0], englishEntry(LEGAL_OK)]]), 0], + ['(c) a legal page: the three licence sentences, one wrapped; a page with no en entry', () => ruleC([glossary('Commercial.'), [LEGAL[0], englishEntry(LEGAL_BAD)], [LEGAL[1], englishEntry('export default 1;\n')]]), 4], ]; function selfTest() { diff --git a/apps/docs/app/[lang]/privacy/page.tsx b/apps/docs/app/[lang]/privacy/page.tsx index 1ecb2c6..4945438 100644 --- a/apps/docs/app/[lang]/privacy/page.tsx +++ b/apps/docs/app/[lang]/privacy/page.tsx @@ -5,11 +5,11 @@ import { baseOptions } from '@/lib/layout.shared'; const content = { en: { title: 'Privacy Policy', - updated: 'Last updated: May 27, 2026', + updated: 'Last updated: October 6, 2026', body: [ { heading: 'Overview', - text: 'ObjectOS is a customer-hosted runtime. When you self-host ObjectOS inside your own infrastructure, ObjectStack AI LLC does not collect, store, or process the data flowing through your deployment. This policy describes the limited information we collect when you interact with our public web properties (objectstack.ai, docs.objectstack.ai) and optional cloud services.', + text: 'ObjectOS runs hosted (ObjectOS Cloud) or self-managed on your own infrastructure (ObjectOS Enterprise). When you run ObjectOS self-managed inside your own infrastructure, ObjectStack AI LLC does not collect, store, or process the data flowing through your deployment. The data handling of ObjectOS Cloud is governed by its service agreement, presented at the time you sign up. This policy describes the limited information we collect when you interact with our public web properties (objectstack.ai, docs.objectstack.ai) and optional cloud services.', }, { heading: 'What we collect', @@ -17,7 +17,7 @@ const content = { }, { heading: 'What we do not collect', - text: 'We do not collect data that lives inside a self-hosted ObjectOS deployment. The runtime does not phone home, and your application records never leave the perimeter you operate.', + text: 'We do not collect data that lives inside a self-managed ObjectOS deployment (ObjectOS Enterprise) or inside a deployment of the open-source ObjectStack runtime, and your application records never leave the perimeter you operate. ObjectOS Self-Managed validates its license online (Enterprise air-gapped licenses are offline-validated); the open-source ObjectStack runtime has no telemetry, no license check, and no update ping.', }, { heading: 'Contact', @@ -28,11 +28,11 @@ const content = { }, 'zh-Hans': { title: '隐私政策', - updated: '最近更新:2026 年 5 月 27 日', + updated: '最近更新:2026 年 10 月 6 日', body: [ { heading: '概述', - text: 'ObjectOS 是一款客户自托管的运行时。当你把 ObjectOS 部署在你自己的基础设施中时,ObjectStack AI LLC 不会收集、存储或处理流经你部署的数据。本政策仅描述你访问我们的公开网站(objectstack.ai、docs.objectstack.ai)及任选的云服务时,我们所收集的有限信息。', + text: 'ObjectOS 以托管方式运行(ObjectOS Cloud),或由你自管部署在自己的基础设施中(ObjectOS Enterprise)。当你在自己的基础设施中自管运行 ObjectOS 时,ObjectStack AI LLC 不会收集、存储或处理流经你部署的数据。ObjectOS Cloud 的数据处理,受其在你注册时呈现的服务协议约束。本政策仅描述你访问我们的公开网站(objectstack.ai、docs.objectstack.ai)及任选的云服务时,我们所收集的有限信息。', }, { heading: '我们会收集什么', @@ -40,7 +40,7 @@ const content = { }, { heading: '我们不会收集什么', - text: '我们不会收集自托管 ObjectOS 部署内部的数据。运行时不会回传任何信息,你的应用数据始终留在你自己的边界内。', + text: '我们不会收集自管 ObjectOS 部署(ObjectOS Enterprise)内部的数据,也不会收集开源 ObjectStack 运行时部署内部的数据;你的应用数据始终留在你自己运营的边界内。ObjectOS Self-Managed 会在线校验许可证(Enterprise 隔离网络许可证为离线校验);开源的 ObjectStack 运行时没有遥测、没有许可证校验、没有更新探活。', }, { heading: '联系方式', diff --git a/apps/docs/app/[lang]/terms/page.tsx b/apps/docs/app/[lang]/terms/page.tsx index cad168a..e80a2ab 100644 --- a/apps/docs/app/[lang]/terms/page.tsx +++ b/apps/docs/app/[lang]/terms/page.tsx @@ -5,19 +5,19 @@ import { baseOptions } from '@/lib/layout.shared'; const content = { en: { title: 'Terms of Service', - updated: 'Last updated: May 27, 2026', + updated: 'Last updated: October 6, 2026', body: [ { heading: 'License', - text: 'ObjectOS is distributed under the Apache License 2.0. You may use, modify, and redistribute the software in accordance with that license. The "ObjectOS" name and logo are trademarks of ObjectStack AI LLC and are not granted under the Apache 2.0 license — see TRADEMARK.md in the repository.', + text: 'ObjectOS is a commercial product with no open-source edition. Your use of ObjectOS is governed by the license or service agreement of the edition you use — ObjectOS Cloud or ObjectOS Enterprise. The contents of the repository behind this documentation site (the documentation and the site code) are licensed under the Apache License 2.0, and the open-source ObjectStack framework and its runtime are licensed under the Apache License 2.0 in their own repository. The "ObjectOS" name and logo are trademarks of ObjectStack AI LLC and are not granted under the Apache 2.0 license — see TRADEMARK.md in the repository.', }, { heading: 'Self-hosted deployments', - text: 'When you run ObjectOS inside your own infrastructure, you are solely responsible for the operation, security, availability, backups, and compliance of that deployment. ObjectStack AI LLC provides no warranty for self-hosted use beyond what the Apache License 2.0 specifies.', + text: 'When you run ObjectOS self-managed inside your own infrastructure (ObjectOS Enterprise), you are solely responsible for the operation, security, availability, backups, and compliance of that deployment, and ObjectStack AI LLC provides no warranty for it beyond what your commercial agreement specifies. When you self-host the open-source ObjectStack runtime instead, it is licensed under the Apache License 2.0, and ObjectStack AI LLC provides no warranty beyond what that license specifies.', }, { heading: 'Hosted services', - text: 'Any hosted services operated by ObjectStack AI LLC (for example, the optional control plane or future SaaS offering) are subject to a separate service agreement that will be presented at the time you sign up. Nothing on this site constitutes such an agreement.', + text: 'Any hosted services operated by ObjectStack AI LLC (for example, ObjectOS Cloud) are subject to a separate service agreement that will be presented at the time you sign up. Nothing on this site constitutes such an agreement.', }, { heading: 'Changes', @@ -32,19 +32,19 @@ const content = { }, 'zh-Hans': { title: '服务条款', - updated: '最近更新:2026 年 5 月 27 日', + updated: '最近更新:2026 年 10 月 6 日', body: [ { heading: '许可', - text: 'ObjectOS 以 Apache License 2.0 发布。你可以在该许可证下使用、修改与再分发本软件。"ObjectOS" 名称与 Logo 为 ObjectStack AI LLC 的商标,不在 Apache 2.0 的授权范围内 —— 详见仓库内的 TRADEMARK.md。', + text: 'ObjectOS 是商业产品,没有开源版本。你对 ObjectOS 的使用,受你所使用版本(ObjectOS Cloud 或 ObjectOS Enterprise)的许可协议或服务协议约束。本文档站点所在仓库的内容(文档与站点代码)以 Apache License 2.0 授权;开源的 ObjectStack 框架及其运行时在其自己的仓库中以 Apache License 2.0 授权。"ObjectOS" 名称与 Logo 为 ObjectStack AI LLC 的商标,不在 Apache 2.0 的授权范围内 —— 详见仓库内的 TRADEMARK.md。', }, { heading: '自托管部署', - text: '当你在自己的基础设施中运行 ObjectOS 时,该部署的运行、安全、可用性、备份与合规性,完全由你自行负责。除 Apache License 2.0 明文约定外,ObjectStack AI LLC 对自托管使用不提供任何保证。', + text: '当你在自己的基础设施中自管运行 ObjectOS(ObjectOS Enterprise)时,该部署的运行、安全、可用性、备份与合规性,完全由你自行负责;除你的商业协议明文约定外,ObjectStack AI LLC 对该部署不提供任何保证。若你自托管的是开源的 ObjectStack 运行时,则其以 Apache License 2.0 授权,除该许可证明文约定外,ObjectStack AI LLC 不提供任何保证。', }, { heading: '托管服务', - text: 'ObjectStack AI LLC 运营的任何托管服务(例如可选的控制台、或未来的 SaaS 服务),适用单独的服务协议,将在你注册时另行呈现。本网站的任何内容均不构成此类协议。', + text: 'ObjectStack AI LLC 运营的任何托管服务(例如 ObjectOS Cloud),适用单独的服务协议,将在你注册时另行呈现。本网站的任何内容均不构成此类协议。', }, { heading: '条款变更',