使用 Astro 实现零构建隐私政策
Zero-build privacy policies with Astro

原始链接: https://www.openpolicy.sh/blog/no-build-astro

## OpenPolicy Astro 集成:简化策略生成 OpenPolicy Astro 集成已简化,消除了之前的复杂性。 过去需要一个插件在构建时生成 Markdown 策略——增加了额外的包和文件管理的摩擦——新的方法将策略**直接在 Astro 组件中编译**。 现在,安装 `@openpolicy/sdk`、`@openpolicy/core` 和 `@openpolicy/renderers`,并在中央 `openpolicy.ts` 配置文件中定义您的策略。 Astro 页面随后利用此配置,使用 frontmatter 编译和渲染策略(隐私、条款、Cookie)为静态 HTML——**无需插件或集成**。 这消除了生成的文件,简化了配置(保持 `astro.config.mjs` 清洁),并减少了依赖项。 该过程是确定性的;使用 Claude 等工具*配置*基于现有文档的策略可确保一致的结果,而 OpenPolicy 则处理实际的策略生成。 生成的 HTML 是静态的,不包含任何客户端 JavaScript。 对于诸如同意跟踪和 PR 自动化之类的先进功能,OpenPolicy+ 提供基于云的扩展和专门的入职服务。

Astro 用户现在可以使用“OpenPolicy”动态生成隐私政策,解决常见的问题:隐私政策过时且不准确。Hacker News 上的讨论集中在,考虑到许多网站很少更新其静态隐私页面,投资于动态系统是否值得。 OpenPolicy 的创建者认为,该工具的主要目的不是*避免*法律问题,而是为了**提高透明度并建立用户信任**。他们指出,许多政策已经多年未更新,不能反映当前的数据实践。 OpenPolicy 的未来版本将包含自动检测功能,以自动跟踪数据使用情况和第三方集成,确保政策始终保持最新。一位评论员指出了隐私政策和使用条款之间的区别。
相关文章

原文

When we launched the OpenPolicy Astro integration in March, it worked by generating Markdown files at build time. You added the plugin to astro.config.mjs, pointed it at an output directory, and Astro imported the generated .md files as components.

// astro.config.mjs — old approach
import { openPolicy } from "@openpolicy/astro";

export default defineConfig({
  integrations: [
    openPolicy({
      formats: ["markdown"],
      outDir: "src/generated/policies",
    }),
  ],
});

It worked. But it added friction: an extra package, a .gitignore entry for the generated directory, and a file-watching step between your config and your page.

The core library now compiles policies directly. You can call it straight from Astro’s frontmatter — no integration, no generated files.

Install

bun add @openpolicy/sdk @openpolicy/core @openpolicy/renderers

No Astro integration needed. No Vite plugin. Three packages.

Define your config

Create src/lib/openpolicy.ts:

import { defineConfig } from "@openpolicy/sdk";

export default defineConfig({
  company: {
    name: "Acme",
    legalName: "Acme, Inc.",
    // ...
  },
  privacy: {
    effectiveDate: "2026-04-01",
    dataCollected: { /* ... */ },
    jurisdictions: ["us", "eu"],
    // ...
  },
  cookie: {
    effectiveDate: "2026-04-01",
    cookies: [ /* ... */ ],
    // ...
  },
  terms: {
    effectiveDate: "2026-04-01",
    governingLaw: { jurisdiction: "Delaware, USA" },
    // ...
  },
});

See the full config schema for all available fields.

The fastest way to fill this out is to paste your existing privacy page (or just describe your app) into Claude and ask it to generate the config. Because the output is deterministic — the same config always produces the same policy — Claude is only configuring, not writing legal text. You review the inputs, OpenPolicy handles the rest.

Render on a dedicated page

Each page finds its policy from the config, compiles it, and renders to HTML — all in the frontmatter:

---
// src/pages/privacy.astro
import { compile, expandOpenPolicyConfig } from "@openpolicy/core";
import { renderHTML } from "@openpolicy/renderers";
import openpolicy from "../lib/openpolicy";

const policies = expandOpenPolicyConfig(openpolicy);
const privacyPolicy = policies.find((p) => p.type === "privacy");

if (!privacyPolicy) {
  throw new Error("Privacy policy not found in config");
}

const policy = renderHTML(compile(privacyPolicy));
---

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>Privacy Policy</title>
  </head>
  <body>
    <div set:html={policy} />
  </body>
</html>

expandOpenPolicyConfig splits the unified config into individual policies. compile runs each through its section builders. renderHTML converts the result to an HTML string. set:html renders it without escaping.

Everything runs at build time. Zero JavaScript ships to the browser.

Terms and cookie pages work the same way — just find type === "terms" or type === "cookie" instead.

astro.config.mjs

import { defineConfig } from "astro/config";

export default defineConfig({});

No integrations. No plugins. Nothing to configure.

Why this is simpler than the old approach

The plugin approach generated intermediate files that lived in your src/ directory. This one doesn’t:

  • No generated files. No src/generated/ directory to create, manage, or exclude from git.
  • No plugin configuration. astro.config.mjs stays empty. No outDir, no formats, no watched paths.
  • No extra package. Drop @openpolicy/astro — only core and renderers are needed at runtime.
  • All three policy types. Privacy, terms, and cookie policies compile from one config. Same defineConfig() call, same pipeline.

The output is the same as before: statically rendered HTML with no client-side JavaScript. The difference is that the compilation happens inline, in the page that uses it, rather than as a side-effect of a plugin watching your filesystem.

The full working example is in the examples/astro directory of the repo.

Going further with OpenPolicy+

If you need more than static generation, OpenPolicy+ extends the core library with cloud-based consent tracking, PR automation (policy linting, compliance checks on every pull request), and hands-on onboarding from our team. Book a demo and we’ll help you get set up.

联系我们 contact @ memedata.com