diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e2b2ff..649528f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,6 +45,9 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile + - name: Check supply-chain policy + run: pnpm run policy:check + - name: Validate Agent Skills specification run: pnpm check:agent-skill-spec diff --git a/package.json b/package.json index ae3ecbf..164f036 100644 --- a/package.json +++ b/package.json @@ -32,11 +32,15 @@ "local:registry": "tsx scripts/serve-local-registry.ts", "release:version": "lerna version --conventional-commits --no-push", "clean": "lerna run clean --if-present", - "bootstrap": "pnpm install" + "bootstrap": "pnpm install", + "policy": "pnpm-policy generate", + "policy:check": "pnpm-policy check" }, "devDependencies": { + "@constructive-io/pnpm-policy": "0.2.1", "@types/node": "^24.10.1", "lerna": "^8.2.4", + "pnpm-policy": "^0.2.2", "tsx": "4.23.1", "typescript": "^5.9.3" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 38452af..52abb9a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,12 +8,18 @@ importers: .: devDependencies: + '@constructive-io/pnpm-policy': + specifier: 0.2.1 + version: 0.2.1 '@types/node': specifier: ^24.10.1 version: 24.13.3 lerna: specifier: ^8.2.4 version: 8.2.4(@types/node@24.13.3)(encoding@0.1.13) + pnpm-policy: + specifier: ^0.2.2 + version: 0.2.2 tsx: specifier: 4.23.1 version: 4.23.1 @@ -888,6 +894,9 @@ packages: '@constructive-io/llm-env@0.3.0': resolution: {integrity: sha512-E/PisqM4wMThQ5ZUE6RNpNfd5IKxJQKvc5VGPe4/9lWJCJC3aJ5Wd+dRbiI8HV3H+uupLlAcJVE8P5r1PxrwVw==} + '@constructive-io/pnpm-policy@0.2.1': + resolution: {integrity: sha512-tAgH3Zgwn2shQXGlfl5zxmtY/tyZw9jYX7QAOFMp5ALKSrxO5xsxZQmckwMqOeHyyV/MZJWzIZ4t/QYznJeVmw==} + '@constructive-io/s3-streamer@2.29.2': resolution: {integrity: sha512-GJ9ph8i3NfgfddAOlIAoj/455Q7XuNrCT2S29xhUi78TijRmBgteU5syE+jPFUAGYHgqEISDJYuMwI+fkCreQQ==} @@ -5819,6 +5828,11 @@ packages: engines: {node: '>=10'} hasBin: true + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + mlly@1.8.2: resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} @@ -5906,6 +5920,9 @@ packages: nested-obj@0.2.2: resolution: {integrity: sha512-M1etu+T6Ai9Bo06L3K3nWD0ytZWltggBGsrxJlOGvMNGlCA4fokUVlbPKoWzsiiRX+PXq6Cb1xFEn4chiyC7MQ==} + nested-obj@0.2.3: + resolution: {integrity: sha512-Py9HdJ/qECkQHvryUaPcaIou6t+U/mkpT66jG8al7jh8Opt3wAuTSSXdPDyY8r1ljEH8a0EH6M4YR28b+RQ8zg==} + next-themes@0.4.6: resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==} peerDependencies: @@ -6364,6 +6381,10 @@ packages: resolution: {integrity: sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==} engines: {node: '>=4'} + pnpm-policy@0.2.2: + resolution: {integrity: sha512-Fj5/ACIZG/AGynTvhT8/7fJTf8ya3jG/Mkq/OMNVZhfTTJ1oniRTsKBHlXGF4sSecvIFqkmeHpn8CsQFwGcENA==} + hasBin: true + postcss-load-config@6.0.1: resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} engines: {node: '>= 18'} @@ -7774,6 +7795,9 @@ packages: engines: {node: '>= 14.6'} hasBin: true + yamlize@0.12.1: + resolution: {integrity: sha512-rU2BN+gmyr0A4yK5i/Ps9JO1MjQGIGUelkaFglc9i4QqEDKU5HfDMdHhNhGMoeNeVw7nJqWpmDiFnxGb6FR5zA==} + yanse@0.2.1: resolution: {integrity: sha512-SMi3ZO1IqsvPLLXuy8LBCP1orqcjOT8VygiuyAlplaGeH2g+n4ZSSyWlA/BZjuUuN58TyOcz89mVkflSqIPxxQ==} @@ -8444,6 +8468,8 @@ snapshots: '@constructive-io/llm-env@0.3.0': {} + '@constructive-io/pnpm-policy@0.2.1': {} + '@constructive-io/s3-streamer@2.29.2': dependencies: '@aws-sdk/client-s3': 3.1092.0 @@ -13476,6 +13502,8 @@ snapshots: mkdirp@1.0.4: {} + mkdirp@3.0.1: {} + mlly@1.8.2: dependencies: acorn: 8.17.0 @@ -13595,6 +13623,8 @@ snapshots: nested-obj@0.2.2: {} + nested-obj@0.2.3: {} + next-themes@0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: react: 19.2.7 @@ -14187,6 +14217,11 @@ snapshots: pluralize@7.0.0: {} + pnpm-policy@0.2.2: + dependencies: + yaml: 2.9.0 + yamlize: 0.12.1 + postcss-load-config@6.0.1(jiti@2.7.0)(postcss@8.5.19)(tsx@4.23.1)(yaml@2.9.0): dependencies: lilconfig: 3.1.3 @@ -15741,6 +15776,12 @@ snapshots: yaml@2.9.0: {} + yamlize@0.12.1: + dependencies: + mkdirp: 3.0.1 + nested-obj: 0.2.3 + yaml: 2.9.0 + yanse@0.2.1: {} yargs-parser@20.2.9: {} diff --git a/pnpm-policy.yaml b/pnpm-policy.yaml new file mode 100644 index 0000000..f9979c2 --- /dev/null +++ b/pnpm-policy.yaml @@ -0,0 +1,45 @@ +# Supply-chain policy for this workspace. The pnpm settings it produces live in +# pnpm-workspace.yaml under the `Managed by pnpm-policy` marker — edit this file, +# then run `pnpm run policy`. `pnpm run policy:check` fails CI when they drift. + +# Third-party releases wait two days. A compromised release is normally reported +# and yanked within hours, so the short wait catches it without stalling upgrades. +minimumReleaseAge: 2d + +# Transitive dependencies must resolve from the registry, not from git or a URL. +blockExoticSubdeps: true + +# The npm accounts WE publish under. Everything they publish skips the wait, so +# this lists accounts we control — nobody else's. +maintainers: + - pyramation + +# Scopes we own outright, emitted as `@scope/*` globs so they also cover packages +# published there tomorrow. +scopes: + - "@constructive-io" + - "@constructive-db" + - "@launchql" + - "@pgpm" + - "@pgpmjs" + - "@pgsql" + +# Resolved from the pinned data package rather than regenerated per repo. +inventory: "@constructive-io/pnpm-policy/inventory.json" + +# Only emit the first-party names this lockfile actually resolves, instead of all +# ~1100 we publish. +intersect: true + +# Dependencies allowed to run install scripts. The value is the reason. +allowBuilds: + "@tailwindcss/oxide": native binary, downloaded at install time + sharp: libvips bindings + unrs-resolver: native resolver binary + esbuild: native binary, fetched by its install script + nx: native task-runner binary + msw: generates the mock service worker script used by tests + +# Third-party escape hatches. A reason is required; `until` expires the waiver so +# `check` makes you re-justify it instead of letting it live forever. +exceptions: [] diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 4a42429..687f585 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,7 +2,65 @@ packages: - apps/* - packages/* -onlyBuiltDependencies: - - '@tailwindcss/oxide' - - sharp - - unrs-resolver +# Managed by pnpm-policy — run `pnpm-policy generate` after editing pnpm-policy.yaml. + +# A third-party release must be 2d old before it can be installed. +# Most malicious releases are found and yanked well inside that window. +minimumReleaseAge: 2880 + +# Exempt from the wait: 6 scope glob(s), 35 first-party package(s). +# First-party membership comes from what pyramation publishes on npm — waiting on your own release protects nothing. +minimumReleaseAgeExclude: + - "@constructive-db/*" + - "@constructive-io/*" + - "@launchql/*" + - "@pgpm/*" + - "@pgpmjs/*" + - "@pgsql/*" + - 12factor-env + - "@agentic-kit/ollama" + - "@agentic-kit/protocol" + - etag-hash + - gql-ast + - graphile-bucket-provisioner-plugin + - graphile-bulk-mutations + - graphile-cache + - graphile-connection-filter + - graphile-i18n + - graphile-llm + - graphile-ltree + - graphile-pg-aggregates + - graphile-plugin-utils + - graphile-postgis + - graphile-presigned-url-plugin + - graphile-realtime-subscriptions + - graphile-search + - graphile-settings + - graphile-upload-plugin + - inflekt + - komoji + - mime-bytes + - nested-obj + - node-type-registry + - pg-ast + - pg-cache + - pg-env + - pg-query-context + - pgsql-deparser + - pnpm-policy + - schema-typescript + - uuid-hash + - yamlize + - yanse + +# The only dependencies permitted to run install scripts. +allowBuilds: + "@tailwindcss/oxide": true # native binary, downloaded at install time + esbuild: true # native binary, fetched by its install script + msw: true # generates the mock service worker script used by tests + nx: true # native task-runner binary + sharp: true # libvips bindings + unrs-resolver: true # native resolver binary + +# Transitive dependencies must come from the registry, not from git or a URL. +blockExoticSubdeps: true