diff --git a/.github/workflows/public-data-flow-contract.yml b/.github/workflows/public-data-flow-contract.yml new file mode 100644 index 0000000..ac8caaa --- /dev/null +++ b/.github/workflows/public-data-flow-contract.yml @@ -0,0 +1,35 @@ +name: Public data-flow contract + +on: + pull_request: + paths: + - "PRIVACY.md" + - "README.md" + - "TERMS.md" + - "scripts/verify-privacy-docs.mjs" + - ".github/workflows/public-data-flow-contract.yml" + push: + branches: + - main + paths: + - "PRIVACY.md" + - "README.md" + - "TERMS.md" + - "scripts/verify-privacy-docs.mjs" + - ".github/workflows/public-data-flow-contract.yml" + +permissions: + contents: read + +jobs: + verify: + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + - name: Verify public data-flow disclosures + run: node scripts/verify-privacy-docs.mjs diff --git a/PRIVACY.md b/PRIVACY.md index 98c9358..4918b51 100644 --- a/PRIVACY.md +++ b/PRIVACY.md @@ -1,139 +1,162 @@ # Privacy Policy **Effective Date**: November 27, 2025 -**Last Updated**: November 27, 2025 + +**Last Updated**: August 30, 2026 ## Overview -MetaEngine MCP Server is designed with privacy as a core principle. This document explains what data is collected, how it's used, and how it's protected. +MetaEngine MCP Server has two execution boundaries. The local MCP server runs on your machine over stdio and handles tool calls and local file writes. Code generation occurs in the hosted MetaEngine API, where each submitted generation payload receives ephemeral processing and the generated files are returned to the local server. + +This policy distinguishes generation content from the anonymous operational metadata retained to operate and support the hosted service. --- -## What Data We Collect +## Generation Data Sent to the Hosted API + +A generation request may contain: -### Code Generation Specifications +- **Semantic type specifications** — structured definitions of classes, interfaces, enums, generics, and related types +- **Converter inputs** — OpenAPI documents or URLs, GraphQL schemas, Protocol Buffer definitions, or SQL DDL +- **Target selection** — the requested language or client framework +- **Generator options** — the options needed to produce the requested output +- **Explicit source content** — source or `customCode` content explicitly included in a generation request is part of that request and is sent to the hosted MetaEngine API +- **MCP package version** — sent in the `X-MCP-Version` request header -When you use MetaEngine MCP Server through Claude Code, Claude Desktop, or any MCP-compatible AI assistant, the following data is sent to the MetaEngine API: +The local MCP server does not automatically scan or upload existing project source files. A generation specification explicitly supplied inline or through `load_spec_from_file` is read locally and sent as the generation payload. Existing source content is sent only when the client or assistant deliberately includes that content in the submitted specification, such as a `customCode` block or custom file. + +--- -- **Type specifications** (JSON structures describing classes, interfaces, types to generate) -- **Language selection** (e.g., TypeScript, Python, Go) -- **Configuration options** (output paths, file naming preferences) +## Generation Content We Do Not Retain -### What We DO NOT Collect +Submitted generation content and generated source files are request-scoped: -- **Your existing source code** — never sent to our servers -- **File contents** — only generation specs are transmitted -- **Personal information** — no names, emails, or identifying data -- **Specification contents** — not logged or saved (ephemeral processing only) -- **Generated code** — returned to you and immediately discarded -- **User identifiers** — anonymous service, no tracking of individual users -- **API keys or credentials** — MetaEngine requires no authentication +- Specification, schema, DDL, Protocol Buffer, source, and `customCode` contents are not persisted or logged +- Generated file contents are returned to the caller and are not persisted or logged +- Validation rejects are recorded only by fixed field category and count; rejected values are not logged +- Failure event traces record only a bounded exception type chain; exception messages are not logged because they can quote submitted content + +The hosted API's generation telemetry pipeline removes caller identity and content-bearing context. It does not retain user or account identifiers, authentication values, IP addresses, location, device or session values, caller-supplied trace or correlation context, caller Host/query/full request URLs, or arbitrary request properties. The service requires no account or API key. --- -## How We Use Data +## Anonymous Operational Metadata We Retain -### Generation Purposes Only +Anonymous operational metadata is retained for service reliability, performance analysis, and support. The request fields and generation-event properties below are the application-level allowlists; Azure's standard telemetry-record, service-resource, and SDK envelope is disclosed separately under **Infrastructure and Third-Party Services**. -Data sent to the MetaEngine API is used exclusively for: +### Request Telemetry -1. **Code generation** — processing type specifications to produce source files -2. **Returning generated code** — sending the output back to your local machine +- **Endpoint family and method** — a normalized value identifying `POST MCP generation` or `POST Converter generation`; the caller Host, path, query, full request URL, and request Source field are removed +- **Request outcome** — request timestamp, duration, HTTP response status/result code, and success outcome +- **Request size** — `Content-Length` converted to kilobytes as `RequestSizeKB` when that header-derived value is available +- **Telemetry reference** — a server-minted 32-character hexadecimal identifier used for the telemetry operation and request; incoming `Request-Id`, `traceparent`, `tracestate`, baggage, `Request-Context`, `Correlation-Context`, `ai_legacyRootId`, and parent/correlation values are stripped or replaced +- **MCP client version (`McpVersion`)** — retained only for MCP generation requests and events; the `X-MCP-Version` value is kept only when it is version-shaped and no more than 32 characters, otherwise it is recorded as `unknown` -### Data Retention +### Generation Event Telemetry -**Code Specifications and Generated Code:** +Generation traces use a fixed non-content message and retain only these classified fields: -Type specifications and generated code are **never saved or logged**: +- **Incident reference (`IncidentId`)** — a server-minted 32-character hexadecimal incident ID, also returned to the caller on errors so a support request can identify the failed operation +- **Converter kind (`Api`)** — the bounded source format `OpenAPI`, `GraphQL`, `Protobuf`, or `SQL` when applicable +- **Target (`Language`, `Target`, or `Framework`)** — the classified target language or client framework, using the field that applies to that generation event +- **Entity counts** — counts only for classes, interfaces, enums, array types, dictionary types, custom files, concrete generic classes, and concrete generic interfaces; names and contents are not retained +- **Fixed request-rejection outcome** — the reason `configuration-not-deserialized` when an MCP request cannot be deserialized; no rejected value or parser message is retained +- **Type-limit outcome** — submitted billable type count (`TypeCount`) and configured maximum (`MaxAllowed`) when the MCP request exceeds that limit +- **Validation outcome** — validation error count and a breakdown over fixed allowlisted JSON-path field categories, capped at 10 distinct fields, plus an omitted-field count when more categories are present; rejected values are not logged +- **Generation outcome** — elapsed generation time, response file count, generated-content UTF-8 byte count, and warning count +- **Failure shape** — exception type chain with up to five causes; exception messages are not logged -- Spec contents exist **only in memory** during the API request -- Generated code is **immediately discarded** after the response is sent -- **No persistent storage** of specification contents or generated code -- **No retention** of your type definitions or output +Application Insights `ExceptionTelemetry`, `EventTelemetry`, and unrecognized telemetry shapes generated during a generation request are dropped rather than retained. -**Performance Telemetry (Anonymous):** +### External Dependency Telemetry -For performance monitoring and API reliability, we collect **anonymous request metadata only**: +When the hosted API resolves an OpenAPI document supplied by URL, it retains only a fixed external-dependency label/type plus the dependency timestamp, duration, success outcome, and status/result code. The submitted URL, domain, path, query, properties, and metrics are redacted. -- ✅ Request timestamp -- ✅ Response latency (processing time) -- ✅ Request size (bytes) -- ✅ HTTP status codes -- ✅ Error types (if any) +This metadata is not tied to an account or user identity. It does not contain submitted specification values or generated file contents. -**NOT Collected:** -- ❌ Type specification contents -- ❌ Generated code contents -- ❌ User identifiers or personal information -- ❌ IP addresses or location data -- ❌ Authentication tokens (none exist) +### Content Not Collected in Operational Logs -This telemetry is **anonymous** and used solely for performance optimization and service reliability monitoring. +- Type specification, schema, DDL, Protocol Buffer, source, or `customCode` contents +- Generated code contents +- Entity, member, file, or user-supplied names +- Validation rejected values or other submitted literals +- Exception messages +- User, account, or authenticated-user identifiers +- IP addresses, location, user-agent, device, or session data +- API keys, credentials, or authentication tokens +- Caller Host, path, query, full request URL, or submitted external-dependency URL/domain +- Caller-supplied `Request-Id`, `traceparent`, `tracestate`, baggage, `Request-Context`, `Correlation-Context`, `ai_legacyRootId`, parent/correlation values, arbitrary properties, metrics, or global context --- -## Data Security +## How We Use Data -### Transmission +Submitted generation data is used only to: -- All API requests use **HTTPS encryption** (TLS 1.2+) -- Data is encrypted in transit between your machine and MetaEngine servers +1. Process the requested specification through the selected generator +2. Return the generated files to the local MCP server -### Processing +Anonymous operational metadata is used only to: -- Data is processed **ephemerally** in server memory -- **No disk writes** of user specifications or generated code -- Server memory is cleared immediately after each request +1. Monitor reliability and performance +2. Diagnose incidents using the correlation ID supplied to the caller +3. Understand aggregate request and output shape without retaining content +4. Monitor the success, status, and timing of redacted external URL resolution --- -## Third-Party Services +## Security -MetaEngine MCP Server does **not** use: +### Transmission + +- Requests to the hosted MetaEngine API use HTTPS encryption +- Generation content is encrypted in transit between the local MCP server and the hosted API -- Analytics services (Google Analytics, Mixpanel, etc.) -- Tracking pixels or cookies -- Third-party data processors -- Advertising networks +### Processing and Storage -The MetaEngine API is self-hosted and does not share data with any third parties. +- Generation content is held only for in-memory request processing and response delivery +- The MetaEngine application does not write submitted generation content or generated source files to persistent storage or application logs +- Only the allowlisted anonymous operational metadata and Azure telemetry-record, service-resource, and SDK envelope disclosed above and below are retained --- ## Local MCP Server -The MetaEngine MCP Server runs **locally on your machine** via npx. The local server: +The npm package runs locally over stdio. It: -- **Only communicates with the MetaEngine API** for code generation -- **Does not send telemetry** or usage data -- **Does not access files** outside the specified output directories -- **Does not make network requests** except to the MetaEngine API +- Receives tool calls from the configured MCP client +- Reads a specification file only when `load_spec_from_file` is called with that path +- Sends generation payloads to the hosted MetaEngine API for ephemeral processing +- Writes returned files to the requested output directory, or returns them inline for a dry run +- Does not automatically scan or upload existing project source files + +Source or `customCode` content explicitly included in a generation payload is part of that payload and is sent to the hosted MetaEngine API. The local server does not send a separate analytics or advertising telemetry stream. --- -## Your Control +## Infrastructure and Third-Party Services -### What You Can Control +The hosted MetaEngine API runs on Microsoft Azure. Azure Monitor/Application Insights receives the sanitized request telemetry, allowlisted generation events, and redacted external-dependency outcomes enumerated in this policy as an infrastructure and operational telemetry processor. -- **Installation**: You choose when to install and configure the MCP server -- **Usage**: The server only runs when invoked by your AI assistant -- **Output paths**: You specify where generated files are written -- **Removal**: Simply remove the MCP server from your configuration to stop using it +Azure Monitor also attaches its standard telemetry-record, service-resource, and SDK envelope, such as record type/timestamp/severity where applicable, Azure resource identity, and Application Insights SDK/version metadata. Those fields identify the retained record, MetaEngine service, and telemetry runtime, not the caller. The application clears the service hostname/role name and the caller user, account, authentication, IP, location, device, session, and correlation context fields described above. -### No Accounts or Tracking +Submitted generation content, generated file contents, validation rejected values, and exception messages are excluded from that telemetry. MetaEngine does not use generation content or operational metadata for advertising or profiling, and the MCP server does not use tracking pixels or cookies. -MetaEngine MCP Server requires: +--- + +## Your Control -- **No account creation** — use immediately without signup -- **No API keys** — completely anonymous usage -- **No user identification** — we cannot identify individual users -- **No usage quotas** — unlimited requests with no tracking +- **Installation** — you choose whether to configure the MCP server +- **Invocation** — generation runs only when your MCP client invokes a generation tool +- **Payload** — you and your MCP client control the specifications and any explicit source content included in each request +- **Output** — you select where returned files are written and can use dry-run mode to receive them inline +- **Removal** — removing the MCP server from your client configuration stops its use --- ## Children's Privacy -MetaEngine MCP Server does not knowingly collect any information from users under the age of 13. The service is designed for software developers and does not target children. +MetaEngine MCP Server is intended for software development and does not knowingly collect personal information from children under 13. --- @@ -141,31 +164,27 @@ MetaEngine MCP Server does not knowingly collect any information from users unde We may update this Privacy Policy from time to time. Changes will be posted at: -- This repository: [PRIVACY.md](https://github.com/meta-engine/mcp-server/blob/main/PRIVACY.md) -- Website: [metaengine.eu/mcp](https://www.metaengine.eu/mcp) +- [The public MCP server repository](https://github.com/meta-engine/mcp-server/blob/main/PRIVACY.md) +- [The MetaEngine MCP website](https://www.metaengine.eu/mcp) -Continued use of MetaEngine MCP Server after changes constitutes acceptance of the updated policy. +Continued use of MetaEngine MCP Server after a change constitutes acceptance of the updated policy. --- ## Contact -If you have questions about this Privacy Policy or data practices: +For privacy or data-practice questions: - **Email**: info@metaengine.eu -- **Issues**: [GitHub Issues](https://github.com/meta-engine/mcp-server/issues) +- **Issues**: [github.com/meta-engine/mcp-server/issues](https://github.com/meta-engine/mcp-server/issues) - **Website**: [metaengine.eu](https://www.metaengine.eu) --- ## Summary -**TL;DR**: - -- ✅ Specs sent to API for code generation -- ✅ **Never saved or logged** — ephemeral processing only -- ✅ No personal data collected -- ✅ No tracking or analytics -- ✅ Free and anonymous -- ✅ HTTPS encrypted transmission -- ✅ Open source MCP protocol (MIT licensed) +- The MCP adapter runs locally over stdio; generation runs in the hosted MetaEngine API +- Submitted specifications and any explicitly included source content receive ephemeral processing +- Submitted generation content and generated file contents are not persisted or logged +- Application-level operational metadata is retained only through the allowlists enumerated in this policy, with Azure's telemetry-record, service-resource, and SDK envelope disclosed separately +- Existing project source files are not scanned or uploaded automatically diff --git a/README.md b/README.md index f4d6b8b..af9054b 100644 --- a/README.md +++ b/README.md @@ -107,9 +107,11 @@ The AI guide is automatically embedded in the tool description on first use — ## Privacy & Pricing -- **Free** — no API key, no signup, unlimited requests -- **Private** — specs sent for generation are never saved or logged (see [PRIVACY.md](./PRIVACY.md)) -- **Local** — MCP server runs on your machine over stdio, MIT licensed +- **Local adapter** — the local MCP server runs on your machine over stdio, started via `npx`; the package is MIT licensed +- **Hosted generation** — every generation payload is sent over HTTPS to the hosted MetaEngine API for ephemeral processing, then generated files return to the local adapter +- **Explicit payload boundary** — the server does not automatically scan or upload existing project source files; source or `customCode` content explicitly included in a generation request is part of that request and is sent to the hosted MetaEngine API +- **Content privacy** — submitted generation content and generated file contents are not persisted or logged; anonymous operational metadata is retained through the application-level allowlists, with Azure's standard telemetry-record, service-resource, and SDK envelope disclosed separately in the [Privacy Policy](./PRIVACY.md) +- **Free access** — no API key or account is required - **Terms** — See [TERMS.md](./TERMS.md) for usage terms --- diff --git a/TERMS.md b/TERMS.md index 3bfde73..70d72e3 100644 --- a/TERMS.md +++ b/TERMS.md @@ -1,7 +1,7 @@ # Terms of Service **Effective Date**: November 27, 2025 -**Last Updated**: November 27, 2025 +**Last Updated**: August 30, 2026 ## Agreement to Terms @@ -11,11 +11,13 @@ By using MetaEngine MCP Server ("the Service"), you agree to these Terms of Serv ## Description of Service -MetaEngine MCP Server is a Model Context Protocol server that generates source code in multiple programming languages (TypeScript, Python, Go, C#, Java, Kotlin, Groovy, Scala) based on type specifications provided by AI assistants. +MetaEngine MCP Server is a Model Context Protocol server that generates source code in multiple programming languages (TypeScript, Python, Go, C#, Java, Kotlin, Groovy, Scala, Swift, PHP, Rust) based on type specifications provided by AI assistants. The Service consists of: -1. **Local MCP Server** - Runs on your machine via npm/npx -2. **MetaEngine API** - Cloud service that processes generation requests +1. **Local MCP Server** — Runs on your machine over stdio via npm/npx +2. **Hosted MetaEngine API** — Receives generation payloads for ephemeral processing and returns generated files + +The local server does not automatically scan or upload existing project source files. Source or `customCode` content explicitly included in a generation request is part of that request and is sent to the hosted MetaEngine API. --- @@ -37,9 +39,10 @@ Full license text: [LICENSE](./LICENSE) The MetaEngine API (code generation service) is provided **free of charge** with: - ✅ **No API key required** - Anonymous usage -- ✅ **No rate limits** - Unlimited requests +- ✅ **Protective rate limits only** - Applied only to prevent abuse - ✅ **No signup required** - Immediate access -- ✅ **No usage tracking** - Requests are not logged +- ✅ **Content-scoped retention** - Submitted generation content and generated file contents are not persisted or logged +- ✅ **Anonymous operations** - Operational metadata is retained as enumerated in the Privacy Policy --- @@ -134,10 +137,11 @@ You are responsible for: See our **[Privacy Policy](./PRIVACY.md)** for details on data handling. **Summary:** -- Specifications sent to the API are processed ephemerally -- **No data is saved or logged** -- Generated code is returned and immediately discarded from our systems -- Your existing code **never leaves your machine** +- The local MCP server runs over stdio; generation payloads are sent to the hosted MetaEngine API for ephemeral processing +- Submitted generation content and generated file contents are not persisted or logged +- Anonymous operational metadata is retained as enumerated in the Privacy Policy +- The server does not automatically scan or upload existing project source files +- Source or `customCode` content explicitly included in a generation payload is part of that payload and is sent to the hosted MetaEngine API --- @@ -260,4 +264,4 @@ Repository: https://github.com/meta-engine/mcp-server By using MetaEngine MCP Server, you acknowledge that you have read, understood, and agree to be bound by these Terms of Service. -**Last Updated**: November 27, 2025 +**Last Updated**: August 30, 2026 diff --git a/scripts/verify-privacy-docs.mjs b/scripts/verify-privacy-docs.mjs new file mode 100644 index 0000000..4ac0693 --- /dev/null +++ b/scripts/verify-privacy-docs.mjs @@ -0,0 +1,221 @@ +import { readFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +class PrivacyDocumentationContract { + constructor(packageRoot, readText, resolvePath) { + this.packageRoot = packageRoot; + this.readText = readText; + this.resolvePath = resolvePath; + } + + verify() { + const documents = new Map( + ["PRIVACY.md", "README.md", "TERMS.md"].map((name) => [ + name, + this.readText(this.resolvePath(this.packageRoot, name), "utf8"), + ]), + ); + const violations = []; + + this.rejectContradictoryClaims(documents, violations); + this.requirePolicyDates(documents, violations); + this.requireProcessingBoundary(documents, violations); + this.requireSourcePayloadDistinction(documents, violations); + this.requireContentRetentionDistinction(documents, violations); + this.requireSupportedLanguageDisclosure(documents.get("TERMS.md"), violations); + this.requireRetentionDisclosure(documents.get("PRIVACY.md"), violations); + + if (violations.length > 0) { + throw new Error( + `MCP privacy documentation contract failed:\n${violations.map((item) => `- ${item}`).join("\n")}`, + ); + } + + console.log("MCP privacy documentation states the hosted processing and retention contract consistently."); + } + + requirePolicyDates(documents, violations) { + const dates = []; + for (const name of ["PRIVACY.md", "TERMS.md"]) { + const match = documents.get(name).match(/\*\*Last Updated\*\*: ([^\n]+)/i); + if (!match) { + violations.push(`${name} is missing its Last Updated date`); + continue; + } + dates.push([name, match[1]]); + const parsed = Date.parse(match[1]); + if (Number.isNaN(parsed)) { + violations.push(`${name} has an invalid Last Updated date: ${match[1]}`); + } else if (parsed < Date.parse("August 30, 2026")) { + violations.push(`${name} has a Last Updated date older than August 30, 2026`); + } + } + if (new Set(dates.map(([, date]) => date)).size > 1) { + const rendered = dates.map(([name, date]) => `${name}=${date}`).join(", "); + violations.push(`PRIVACY.md and TERMS.md have different Last Updated dates: ${rendered}`); + } + } + + rejectContradictoryClaims(documents, violations) { + const forbiddenClaims = [ + /\brequests are not logged\b/i, + /\bno data is (?:saved(?: or logged)?|logged)\b/i, + /\bnothing is uploaded\b/i, + /\bexisting code never leaves your machine\b/i, + /\b(?:specifications?|schemas?|generation inputs?) never leave(?:s)? (?:the|your) (?:machine|page)\b/i, + ]; + + for (const [name, text] of documents) { + for (const claim of forbiddenClaims) { + if (claim.test(text)) { + violations.push(`${name} contains contradictory absolute claim ${claim}`); + } + } + } + } + + requireProcessingBoundary(documents, violations) { + for (const [name, text] of documents) { + this.requirePatterns( + name, + text, + [ + ["local stdio MCP boundary", /local[^.\n]{0,120}stdio/i], + ["hosted MetaEngine API boundary", /hosted MetaEngine API/i], + ["ephemeral processing", /ephemeral(?:ly)? process/i], + ], + violations, + ); + } + } + + requireSourcePayloadDistinction(documents, violations) { + for (const [name, text] of documents) { + this.requirePatterns( + name, + text, + [ + [ + "no automatic existing-source upload", + /does not automatically scan or upload existing project source files/i, + ], + [ + "explicit source/customCode payload exception", + /source or `customCode` content explicitly included in a generation (?:request|payload)[^.\n]*sent/i, + ], + ], + violations, + ); + } + } + + requireContentRetentionDistinction(documents, violations) { + for (const [name, text] of documents) { + this.requirePatterns( + name, + text, + [ + [ + "content-scoped non-retention", + /submitted generation content and generated file contents are not persisted or logged/i, + ], + ["retained anonymous operational metadata", /anonymous operational metadata is retained/i], + ], + violations, + ); + } + } + + requireSupportedLanguageDisclosure(terms, violations) { + this.requirePatterns( + "TERMS.md", + terms, + [ + [ + "complete supported language set", + /TypeScript, Python, Go, C#, Java, Kotlin, Groovy, Scala, Swift, PHP, Rust/, + ], + ], + violations, + ); + } + + requireRetentionDisclosure(privacy, violations) { + this.requirePatterns( + "PRIVACY.md", + privacy, + [ + ["normalized MCP endpoint", /POST MCP generation/i], + ["normalized converter endpoint", /POST Converter generation/i], + ["request timestamp", /request timestamp/i], + ["request duration", /request timestamp, duration/i], + ["request status and success", /HTTP response status\/result code, and success outcome/i], + ["Content-Length-derived request size", /Content-Length[^.\n]*kilobytes[^.\n]*RequestSizeKB/i], + ["server-minted telemetry ID", /server-minted 32-character hexadecimal identifier[^.\n]*operation and request/i], + ["request Source removal", /full request URL, and request Source field are removed/i], + ["incoming trace-context removal", /incoming `Request-Id`[^.\n]*`traceparent`[^.\n]*`tracestate`[^.\n]*baggage[^.\n]*`Request-Context`[^.\n]*`Correlation-Context`[^.\n]*`ai_legacyRootId`[^.\n]*stripped or replaced/i], + ["server-minted incident ID", /IncidentId[^.\n]*server-minted 32-character hexadecimal incident ID/i], + [ + "MCP-only bounded client version", + /MCP client version \(`McpVersion`\)[^.\n]*retained only for MCP generation requests and events[^.\n]*version-shaped[^.\n]*no more than 32 characters[^.\n]*unknown/i, + ], + ["bounded converter Api field", /Converter kind \(`Api`\)[^.\n]*OpenAPI[^.\n]*GraphQL[^.\n]*Protobuf[^.\n]*SQL/i], + ["classified Language/Target/Framework fields", /Target \(`Language`, `Target`, or `Framework`\)[^.\n]*classified target language or client framework/i], + [ + "all entity-count kinds", + /classes, interfaces, enums, array types, dictionary types, custom files, concrete generic classes, and concrete generic interfaces/i, + ], + ["fixed rejection reason", /configuration-not-deserialized/i], + ["type-limit count", /billable type count[^.\n]*TypeCount/i], + ["type-limit maximum", /configured maximum[^.\n]*MaxAllowed/i], + ["validation error count", /validation error count/i], + ["fixed JSON-path fields", /fixed allowlisted JSON-path field categories/i], + ["ten-field cap", /(?:maximum of|capped at) 10 distinct fields/i], + ["omitted-field count", /omitted-field count/i], + ["elapsed generation time", /elapsed generation time/i], + ["response file count", /response file count/i], + ["response byte count", /generated-content UTF-8 byte count/i], + ["warning count", /warning count/i], + ["bounded exception type chain", /exception type chain[^.\n]*(?:maximum of|up to) five causes/i], + ["no exception messages", /exception messages?[^.\n]*(?:not|never) logged/i], + ["no rejected values", /rejected values?[^.\n]*(?:not|never) logged/i], + ["fixed trace message", /Generation traces use a fixed non-content message/i], + ["dropped unsafe telemetry shapes", /ExceptionTelemetry[^.\n]*EventTelemetry[^.\n]*unrecognized telemetry shapes[^.\n]*dropped/i], + ["redacted dependency outcome", /fixed external-dependency label\/type[^.\n]*timestamp, duration, success outcome, and status\/result code/i], + ["dependency URL redaction", /submitted URL, domain, path, query, properties, and metrics are redacted/i], + ["caller identity/context removal", /does not retain user or account identifiers[^.\n]*IP addresses[^.\n]*device or session values[^.\n]*trace or correlation context/i], + ["Azure hosting", /hosted MetaEngine API runs on Microsoft Azure/i], + [ + "Azure operational telemetry processor", + /Azure Monitor\/Application Insights receives[^.\n]*sanitized request telemetry[^.\n]*allowlisted generation events[^.\n]*redacted external-dependency outcomes[^.\n]*infrastructure and operational telemetry processor/i, + ], + ["Azure telemetry/service/SDK envelope", /standard telemetry-record, service-resource, and SDK envelope[^.\n]*record type\/timestamp\/severity[^.\n]*Azure resource identity[^.\n]*Application Insights SDK\/version metadata/i], + ["service role-name removal", /clears the service hostname\/role name/i], + [ + "no advertising or profiling use", + /does not use generation content or operational metadata for advertising or profiling/i, + ], + [ + "generation content excluded from Azure telemetry", + /submitted generation content, generated file contents, validation rejected values, and exception messages are excluded from that telemetry/i, + ], + ], + violations, + ); + } + + requirePatterns(name, text, requirements, violations) { + for (const [label, pattern] of requirements) { + if (!pattern.test(text)) { + violations.push(`${name} is missing ${label}`); + } + } + } +} + +new PrivacyDocumentationContract( + resolve(dirname(fileURLToPath(import.meta.url)), ".."), + readFileSync, + resolve, +).verify();