Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7

# The vendored OpenAPI spec is a copy of the engine's. Without the source
# beside it there is nothing to compare against, and a drift check that
Expand All @@ -26,7 +26,7 @@ jobs:
# openapi-source.json rather than at whatever is currently on main. See
# that script's header for why the pin exists.
- name: Checkout dpp-engine (source of the vendored API spec)
uses: actions/checkout@v4
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
repository: odal-node/dpp-engine
path: .dpp-engine
Expand All @@ -37,7 +37,7 @@ jobs:
run: corepack enable

- name: Set up Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
with:
node-version: 24.18.0
cache: pnpm
Expand Down
161 changes: 161 additions & 0 deletions .github/workflows/sync-openapi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
name: Sync API spec pin

# Proposes a bump of the vendored OpenAPI spec. It opens a pull request; it
# never pushes to main, and it never turns an unrelated pull request red.
#
# WHY A PULL REQUEST AND NOT A GATE
#
# site/dpp-docs/scripts/sync-openapi.mjs compares the vendored copy against the
# spec at the commit recorded in openapi-source.json — deliberately not against
# whatever is currently on the engine's main. Pinning is what keeps this repo's
# CI deterministic: an unrelated merge in the engine cannot redden a pull
# request here, and a correction that must land in both repos does not deadlock
# on which one merges first. See that script's header.
#
# This job does not change that. The pinned-commit check in ci.yml is untouched
# and remains the gate. All this does is notice the pin has fallen behind and
# put the bump in front of a human as a reviewable diff — which is what the
# "reviewable line in a diff" in openapi-source.json's comment describes, done
# on a schedule instead of from memory.

on:
schedule:
# 03:17 UTC — off the hour, so this is not queued behind everything else
# that runs at midnight.
- cron: '17 3 * * *'
workflow_dispatch:

# Scoped on the job, not the workflow, and this is the only workflow in the repo
# that writes anything.
permissions:
contents: read

jobs:
sync:
name: Bump the vendored API spec
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7

# Full history: the sync script reads the spec out of the engine's git
# objects, and records the commit it copied from.
- name: Checkout dpp-engine (source of the vendored API spec)
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
repository: odal-node/dpp-engine
path: .dpp-engine
fetch-depth: 0
persist-credentials: false

- name: Enable Corepack
run: corepack enable

- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
with:
node-version: 24.18.0
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Sync the vendored spec and its pin
run: pnpm run sync:openapi
env:
DPP_ENGINE_DIR: ${{ github.workspace }}/.dpp-engine

- name: Is there anything to propose?
id: diff
run: |
if git diff --quiet -- site/dpp-docs/public/openapi.yaml site/dpp-docs/openapi-source.json; then
echo "changed=false" >> "$GITHUB_OUTPUT"
echo "The pin is already current. Nothing to propose."
else
echo "changed=true" >> "$GITHUB_OUTPUT"
git --no-pager diff --stat -- site/dpp-docs/public/openapi.yaml site/dpp-docs/openapi-source.json
fi

# The same checks ci.yml runs, executed here rather than trusted to run
# later. A pull request opened with GITHUB_TOKEN does not trigger workflow
# runs — GitHub blocks that to stop workflows recursing — so without this
# the bump would arrive with no evidence attached. If SYNC_PAT is
# configured the pull request does get a normal CI run as well, and this
# step is then belt and braces.
- name: Verify the bumped copy builds and passes its own checks
if: steps.diff.outputs.changed == 'true'
run: |
pnpm run check:openapi
pnpm -r build
pnpm -r check
pnpm run check:links
pnpm run check:leakage
env:
DPP_ENGINE_DIR: ${{ github.workspace }}/.dpp-engine

# A PAT makes the pull request trigger CI like a human's would, which
# matters because main requires passing checks and GITHUB_TOKEN pull
# requests never get them. Without the secret this still works — the
# request is opened, and a reviewer closing and reopening it starts CI.
- name: Choose a token
if: steps.diff.outputs.changed == 'true'
id: token
env:
SYNC_PAT: ${{ secrets.SYNC_PAT }}
run: |
if [ -n "$SYNC_PAT" ]; then
echo "ci_runs=true" >> "$GITHUB_OUTPUT"
else
echo "ci_runs=false" >> "$GITHUB_OUTPUT"
echo "::warning::SYNC_PAT is not configured. The pull request will open but CI will not run on it automatically; close and reopen it to start a run."
fi

# A fixed branch, force-updated. A second night's bump supersedes the
# first rather than opening a second request against the same file.
- name: Open or update the pull request
if: steps.diff.outputs.changed == 'true'
env:
GH_TOKEN: ${{ secrets.SYNC_PAT || github.token }}
CI_RUNS: ${{ steps.token.outputs.ci_runs }}
run: |
set -euo pipefail
BRANCH=chore/sync-openapi-pin
PIN=$(node -p "require('./site/dpp-docs/openapi-source.json').commit")
SHORT=${PIN:0:9}

git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git checkout -b "$BRANCH"
git add site/dpp-docs/public/openapi.yaml site/dpp-docs/openapi-source.json
git commit -m "chore(docs): bump the vendored API spec to ${SHORT}"
git push --force origin "$BRANCH"

if gh pr view "$BRANCH" --json number >/dev/null 2>&1; then
echo "Pull request already open for $BRANCH; the force-push updated it."
exit 0
fi

cat > /tmp/pr-body.md <<EOF
The vendored \`public/openapi.yaml\` and its pin now point at
\`odal-node/dpp-engine@${SHORT}\`.

Opened automatically because the pin had fallen behind. The
pinned-commit drift check in CI is unchanged — this only moves the
pin, deliberately and reviewably.

Build, checks, links, leakage and the spec drift check were all run
against this content in the sync job before this request was opened.
EOF

if [ "$CI_RUNS" != "true" ]; then
printf '\n> CI does not run automatically on a pull request opened with the default token. Close and reopen this request to start a run.\n' >> /tmp/pr-body.md
fi

gh pr create \
--base main \
--head "$BRANCH" \
--title "chore(docs): bump the vendored API spec to ${SHORT}" \
--body-file /tmp/pr-body.md
35 changes: 30 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Each Astro project has its own `package.json` and its own Cloudflare Pages proje

## Independent Deploys, Shared Brand

The architectural commitment of this repository is *independence at the deployment layer, coherence at the brand layer*. Pushing to `main` with a change that only touches `site/dpp-docs/src/content/docs/quick-start.mdx` produces a single docs deploy and zero landing deploys. Pushing a change that touches `packages/brand-tokens/` produces two deploys, because a token change genuinely should re-render both surfaces. This is enforced via Cloudflare Pages [build watch paths](https://developers.cloudflare.com/pages/configuration/build-watch-paths/) rather than at the Git layer.
The architectural commitment of this repository is *independence at the deployment layer, coherence at the brand layer*. A promotion into `main` that only touches `site/dpp-docs/src/content/docs/quick-start.mdx` produces a single docs deploy and zero landing deploys. One that touches `packages/brand-tokens/` produces two deploys, because a token change genuinely should re-render both surfaces. This is enforced via Cloudflare Pages [build watch paths](https://developers.cloudflare.com/pages/configuration/build-watch-paths/) rather than at the Git layer.

---

Expand All @@ -34,7 +34,7 @@ dpp-web/
├── pnpm-lock.yaml # single lockfile for the whole workspace
├── README.md # this file
├── public/brand/ # canonical brand assets (marks, favicon, og)
├── scripts/ # CI gates: link crawler, leakage scan
├── packages/
│ └── brand-tokens/ # @odal/brand-tokens — colour, type, spacing
Expand Down Expand Up @@ -66,10 +66,21 @@ pnpm dev:docs # http://localhost:4321 → site/dpp-docs
# Build for production (same command Cloudflare runs)
pnpm -r build

# Type-check + broken-link check
# Type-check templates and content-collection references
pnpm -r check

# The gates CI runs. All four need a build first, except the leakage scan.
pnpm run check:links # crawl both dist trees for internal links that 404
pnpm run check:leakage # internal vocabulary / private-repo paths, incl. public/
pnpm run check:openapi # vendored API spec still matches its pinned engine commit
pnpm audit --audit-level critical
```

`pnpm -r check` does **not** check links, and never did — a markdown link target is an opaque
string to `astro check`. That is why `check:links` exists separately and reads the built output
rather than the source: four `[Licensing](/engine/licensing)` links once passed `check` and
404'd in production.

Prerequisites: Node.js 22.13+ (LTS 24 recommended — pnpm 11 requires `node:sqlite`, unavailable before 22.13) and pnpm (managed via [corepack](https://nodejs.org/api/corepack.html) — the exact version is pinned in `package.json` `packageManager`).

---
Expand All @@ -82,13 +93,27 @@ The [`dpp-core`](https://github.com/odal-node/dpp-core) repository (Apache-2.0)

The [`dpp-engine`](https://github.com/odal-node/dpp-engine) repository (BSL-1.1, with a production self-host grant) holds the deployment layer — HTTP services, persistence, authentication, telemetry, the public resolver, the Wasm plugin sandbox. The docs site documents `dpp-engine`; it does not contain its source.

The relationship between the repositories — the open-core boundary, the dependency direction, the licensing rationale — is covered on the docs site under [Design Principles](https://docs.odal-node.io/design/open-core) and in the parent project's strategy documents.
The relationship between the repositories — the open-core boundary, the dependency direction, the licensing rationale — is covered on the docs site under [Core Concepts](https://docs.odal-node.io/core-concepts) and [Licensing](https://docs.odal-node.io/getting-started/licensing), and in the parent project's strategy documents.

---

## Status

The original phased build (workspace foundations → landing MVP → docs IA → polish) is complete through its first three phases, and the **June 2026 redesign** re-skinned both sites onto the navy/ice brand, replaced retired messaging with *"Signed by you. Verified by anyone."*, moved editable content into data files, and corrected stale claims. What remains before public launch: the Lighthouse/a11y pass and deployment. `LICENSE` is settled (Apache-2.0) and CI (`.github/workflows/ci.yml`, gating `pnpm -r build` + `pnpm -r check` on every push/PR) is in place.
The original phased build (workspace foundations → landing MVP → docs IA → polish) is complete through its first three phases, and the **June 2026 redesign** re-skinned both sites onto the navy/ice brand, replaced retired messaging with *"Signed by you. Verified by anyone."*, and moved editable content into data files. `LICENSE` is settled (Apache-2.0).

An **August 2026 audit** of both sites read every published page against primary regulatory text and against the engine's source. It found a delegated act that does not exist described as adopted, roughly twenty misattributed citations, four security-property claims the code contradicted, and a registry described as unbuilt eight months after it went live. Those are corrected; the findings register lives outside this repository.

What remains before public launch: the Lighthouse/axe pass, a runtime check that the API reference does not relay requests through a third-party proxy, and a named data controller in the privacy policy — which is blocked on a registered entity existing, not on a copy edit.

## How changes land

`main` is what Cloudflare Pages publishes, so nothing lands on it directly.

- **`staging`** is the integration branch. Work branches off it and merges back through a pull request.
- Promotion is a second pull request, `staging` → `main`, reviewed on its own.
- `main` carries a ruleset matching the other repositories: pull request required, squash-only, CI must pass, no force-push, no deletion, and **no bypass for anyone** — including the owner.

CI (`.github/workflows/ci.yml`) runs build, type-check, and the four gates listed above on every push and pull request, with the workflow token scoped to `contents: read`.

---

Expand Down
Loading