diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..e4132ff --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,466 @@ +name: Release + +# Cutting a release is a tag push and nothing else. Every artifact below is produced from the +# tagged tree by this workflow, so nothing a maintainer built on a laptop can reach the release. +# +# workflow_dispatch runs the same build and the same provenance over the same subjects, and stops +# before anything is published. That makes the pipeline provable while no tag exists: a tag that +# fails halfway has to be deleted and re-pushed, and a deleted tag is a bad look on a project +# whose pitch is durable evidence. +on: + push: + tags: + - "v*" + workflow_dispatch: + +# Workflow-wide floor. A job added later that forgets its own block inherits read-only instead of +# the repository default, which on many repositories is read-write: a compromised step would +# otherwise hold a token that can push commits, move tags, or open releases. +permissions: + contents: read + +env: + # Exact, not a range. A stranger who rebuilds the verifier and compares checksums is comparing + # against these bytes, and Go's output changes between patch releases. `go-version-file` is + # right for ci.yml, where the question is "does this still build under the Go we claim", and + # wrong here, where the question is "does this produce the same bytes as the release". + GO_VERSION: "1.22.12" + # The engines floor, not the latest Node. Building and testing the release on the oldest + # runtime we advertise is the only thing that keeps that claim from being decorative. + NODE_VERSION: "22.12.0" + # Release tooling, pinned and invoked through npx. It is not a devDependency because it is not + # a build input: nothing in src/ or tests/ needs it, and adding it would put a large tree into + # every contributor's install to serve one step that runs a few times a year. + CYCLONEDX_VERSION: "6.0.0" + # npm Trusted Publishing over OIDC needs npm 11.5.1 or newer. Node 22.12 ships npm 10, which + # fails with an authentication error that reads like a misconfigured token rather than an old + # client, so the version is pinned here instead of being inherited and hoped about. + # + # Held on the 11.x line on purpose. npm 12 declares engines ^22.22.2 || ^24.15.0 || >=26.0.0, + # which the 22.12.0 floor above does not satisfy, so installing it here would fail on a real + # tag and only on a real tag. 11.19.0 accepts ^20.17.0 || >=22.9.0 and clears the OIDC floor. + NPM_VERSION: "11.19.0" + IMAGE_NAME: ghcr.io/reesebuilt/agentwall + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + version: ${{ steps.meta.outputs.version }} + tag: ${{ steps.meta.outputs.tag }} + tarball: ${{ steps.meta.outputs.tarball }} + is-release: ${{ steps.meta.outputs.is-release }} + hashes: ${{ steps.checksums.outputs.hashes }} + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Setup Node + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: ${{ env.NODE_VERSION }} + cache: npm + + - name: Setup Go + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 + with: + go-version: ${{ env.GO_VERSION }} + # Zero requirements means no dependency graph to cache and no go.sum for the cache key + # to hash. setup-go's cache path wants that file and warns when it is absent. + cache: false + + # A tag names a version and package.json names a version. When they disagree, every + # downstream artifact disagrees with something: the tarball says one number, the release + # page says another, and the verifier binary says a third. Fail here, where the only cost + # is deleting an unpushed tag. + - name: Resolve version + id: meta + run: | + set -euo pipefail + pkg_version="$(node -p 'require("./package.json").version')" + if [ "${GITHUB_REF_TYPE}" = "tag" ]; then + tag="${GITHUB_REF_NAME}" + version="${tag#v}" + if [ "$version" != "$pkg_version" ]; then + echo "tag ${tag} implies version ${version}, but package.json says ${pkg_version}." >&2 + echo "bump package.json (and verifier/report.go) or move the tag; do not ship both numbers." >&2 + exit 1 + fi + is_release=true + else + version="$pkg_version" + tag="v${version}" + is_release=false + fi + { + echo "version=${version}" + echo "tag=${tag}" + echo "is-release=${is_release}" + echo "tarball=reesebuilt-agentwall-${version}.tgz" + } >> "$GITHUB_OUTPUT" + echo "building ${tag} (release=${is_release})" + + - name: Install + run: npm ci + + - name: Lint (type-check) + run: npm run lint + + - name: Build + run: npm run build + + - name: Test + run: npm test + + - name: Build verifier for conformance + run: go build -o agentwall-verify . + working-directory: verifier + + # The release's headline claim is that a second implementation agrees about the evidence + # format. Re-running the corpus here means the binaries attached to the release are the + # same ones that agreed, rather than binaries built from a tree that merely passed CI once. + - name: Conformance (typescript and go) + run: node scripts/conformance.js + + - name: Stage release directory + run: mkdir -p dist-release + + # Publishing the exact bytes that were checksummed and attested, rather than re-packing at + # publish time, is the whole point: otherwise the provenance describes an artifact nobody + # can prove is the one on the registry. + - name: Pack npm tarball + run: | + set -euo pipefail + npm pack --pack-destination dist-release + test -f "dist-release/${TARBALL}" + env: + TARBALL: ${{ steps.meta.outputs.tarball }} + + - name: Generate CycloneDX SBOM + run: | + set -euo pipefail + npx --yes "@cyclonedx/cyclonedx-npm@${CYCLONEDX_VERSION}" \ + --omit dev \ + --output-file dist-release/sbom.cdx.json + + # An SBOM nobody checks is decoration. cyclonedx-npm builds its component list from the + # installed tree, and a package that is not hoisted to the top level can be dropped from + # that list while its own dependencies are kept, which produces a plausible-looking SBOM + # with a hole in it. This compares the SBOM against npm's own view of the production tree + # and fails when a package name is absent entirely. + # + # Known limit, deliberately not fatal: when one package is installed at two versions, the + # generator lists one of them. The name is still present, so a reader knows the dependency + # exists; a missing name is the failure that hides a whole component, and that is what this + # gate catches. + - name: SBOM covers the production tree + run: | + set -euo pipefail + npm ls --omit=dev --all --json > npm-tree.json + node - <<'NODE' + const fs = require("node:fs"); + const tree = JSON.parse(fs.readFileSync("npm-tree.json", "utf8")); + const sbom = JSON.parse(fs.readFileSync("dist-release/sbom.cdx.json", "utf8")); + const installed = new Map(); + (function walk(node) { + for (const [name, child] of Object.entries(node.dependencies ?? {})) { + if (!installed.has(name)) installed.set(name, new Set()); + installed.get(name).add(child.version); + walk(child); + } + })(tree); + const listed = new Set( + (sbom.components ?? []).map((c) => (c.group ? `${c.group}/${c.name}` : c.name)) + ); + const missing = [...installed.keys()].filter((name) => !listed.has(name)); + const partial = [...installed.entries()] + .filter(([name, versions]) => listed.has(name) && versions.size > 1) + .map(([name, versions]) => `${name} (${[...versions].join(", ")})`); + console.log(`npm reports ${installed.size} production packages; SBOM lists ${listed.size} components.`); + if (partial.length > 0) { + console.log(`installed at multiple versions, one recorded: ${partial.join("; ")}`); + } + if (missing.length > 0) { + console.error(`SBOM omits these production packages entirely: ${missing.join(", ")}`); + process.exit(1); + } + NODE + + # CGO_ENABLED=0 makes the binaries static, so they run on a machine with no toolchain and + # no libc of a particular vintage. -trimpath removes the builder's absolute paths, which is + # what makes two people's builds of the same source produce the same bytes. + - name: Cross-compile verifier binaries + run: | + set -euo pipefail + for target in linux/amd64 linux/arm64 darwin/amd64 darwin/arm64 windows/amd64; do + goos="${target%/*}" + goarch="${target#*/}" + ext="" + [ "$goos" = "windows" ] && ext=".exe" + CGO_ENABLED=0 GOOS="$goos" GOARCH="$goarch" GOTOOLCHAIN=local \ + go build -trimpath \ + -ldflags "-s -w -X main.verifierVersion=${VERSION}" \ + -o "../dist-release/agentwall-verify-${goos}-${goarch}${ext}" . + done + working-directory: verifier + env: + VERSION: ${{ steps.meta.outputs.version }} + + # The -X flag is silently ignored when it names a symbol that is not a string var, so a + # refactor that turns verifierVersion back into a const would produce binaries reporting a + # stale version with a green build. Asking the binary is the only way to know it took. + - name: Verifier binary reports the release version + run: | + set -euo pipefail + chmod +x dist-release/agentwall-verify-linux-amd64 + reported="$(dist-release/agentwall-verify-linux-amd64 --version)" + echo "$reported" + if [ "$reported" != "agentwall-verify ${VERSION}" ]; then + echo "binary reports '${reported}', expected 'agentwall-verify ${VERSION}'." >&2 + echo "the -ldflags stamp did not take; check that verifierVersion is a var." >&2 + exit 1 + fi + env: + VERSION: ${{ steps.meta.outputs.version }} + + # Sorted with a fixed collation so the file is byte-identical across runs of the same + # inputs, and so the SLSA subject list below is stable. + # + # The file list is captured into an array before checksums.txt is opened, because a shell + # redirection creates its target before the command on the left runs: staging the list in + # a file inside this directory put that staging file into its own checksum list, and + # `sha256sum -c` then failed on a correct download by looking for a file the release does + # not contain. + - name: Checksums + id: checksums + run: | + set -euo pipefail + cd dist-release + mapfile -t files < <( + find . -maxdepth 1 -type f ! -name checksums.txt -printf '%P\n' | LC_ALL=C sort + ) + sha256sum "${files[@]}" > checksums.txt + cat checksums.txt + echo "hashes=$(base64 -w0 < checksums.txt)" >> "$GITHUB_OUTPUT" + + - name: Upload release artifacts + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: release-artifacts + path: dist-release/ + # An upload that silently matches nothing leaves a green job and no artifact, which is + # the failure mode this step exists to make visible. + if-no-files-found: error + + # Exception to the SHA-pinning rule, and the only one in this repository: the SLSA generator + # resolves its own ref against the release tag it was published under and refuses to run when + # it is called by digest, so a SHA pin here fails closed rather than merely going unverified. + # The tag is what its own verification expects, and slsa-verifier checks the builder identity + # afterwards, which is the property the pin would have been protecting. + provenance: + needs: [build] + permissions: + # Read the workflow path that goes into the attestation. + actions: read + # Sign the provenance against the workflow's own OIDC identity. + id-token: write + # Read only. Inside the generator, contents: write belongs to its upload-assets job, which + # is guarded by `if: inputs.upload-assets` and therefore never runs here: the release job + # below attaches the provenance, so one job stays in charge of what the release contains. + contents: read + uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0 + with: + base64-subjects: ${{ needs.build.outputs.hashes }} + upload-assets: false + + release: + needs: [build, provenance] + if: needs.build.outputs.is-release == 'true' + runs-on: ubuntu-latest + permissions: + # Create the release and attach assets to it. + contents: write + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Download build artifacts + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: release-artifacts + path: dist-release + + - name: Download provenance + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: ${{ needs.provenance.outputs.provenance-name }} + path: dist-release + + # The changelog section is the release notes. Keeping one source means the release page + # cannot drift into marketing that the repository does not say. + - name: Compose release notes + run: | + set -euo pipefail + awk -v ver="$VERSION" ' + $0 ~ "^## \\[" ver "\\]" { inside = 1; next } + inside && /^## \[/ { exit } + inside { print } + ' CHANGELOG.md > notes.md + if [ ! -s notes.md ]; then + echo "CHANGELOG.md has no [${VERSION}] section; the release would have empty notes." >&2 + exit 1 + fi + cat >> notes.md < v.split(".").map(Number); + const [a, b] = [parse(have), parse(floor)]; + const older = a[0] - b[0] || a[1] - b[1] || a[2] - b[2]; + if (older < 0) { + console.error(`npm ${have} cannot use Trusted Publishing; ${floor} is the floor.`); + console.error("NPM_VERSION did not install, most likely because this node fails its engines range."); + process.exit(1); + } + ' "$actual" + + - name: Download build artifacts + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: release-artifacts + path: dist-release + + # Publishing the packed tarball, not the working tree, is what makes the checksum and the + # SLSA attestation above describe the artifact that actually lands on the registry. + - name: Publish to npm + run: npm publish "dist-release/${TARBALL}" --provenance --access public + env: + TARBALL: ${{ needs.build.outputs.tarball }} + + docker: + needs: [build, release] + if: needs.build.outputs.is-release == 'true' + runs-on: ubuntu-latest + permissions: + # Push the image to GitHub Container Registry. + packages: write + # Keyless cosign signing against the workflow's OIDC identity, same reasoning as npm above. + id-token: write + contents: read + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Image metadata + id: metadata + uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0 + with: + images: ${{ env.IMAGE_NAME }} + tags: | + type=raw,value=${{ needs.build.outputs.tag }} + type=raw,value=latest + + - name: Build and push + id: push + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 + with: + context: . + file: ./Dockerfile + # One architecture, because one architecture is what gets built and exercised. An + # arm64 manifest nobody has run is a support claim, not a build artifact. + platforms: linux/amd64 + push: true + tags: ${{ steps.metadata.outputs.tags }} + labels: ${{ steps.metadata.outputs.labels }} + provenance: true + + - name: Install cosign + uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 + + # Sign the digest, not the tag. A tag is a mutable pointer, so a signature over a tag says + # nothing about which bytes were signed once the tag moves. + - name: Sign the image + run: cosign sign --yes "${IMAGE_NAME}@${DIGEST}" + env: + DIGEST: ${{ steps.push.outputs.digest }} diff --git a/CHANGELOG.md b/CHANGELOG.md index ead4db7..1a98032 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,50 @@ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.ht ## [Unreleased] +## [0.2.0] - 2026-08-05 + +The first tagged release. It freezes the on-disk evidence format and makes that format +checkable by a program that shares no code with Agentwall. + +Upgrade if you care whether the audit log can be quietly rewritten. Before this release the +hash chain was verifiable only by the same TypeScript that wrote it, which means a bug in the +writer was invisible to the reader. Now the format is specified in `docs/audit-format.md`, a +second implementation in Go verifies it, and the two are held against a shared corpus on every +commit. Existing audit files stay verifiable: records written before this release carry no +canonical-form marker and are still accepted through the legacy path. + +Nothing about the shipped posture changed. Monitor-first is still the default, and no default +decision, policy file, or enforcement behavior moved in this release. + ### Added +- Canonical form `cu1`, recorded in each record's `integrity.canon`. Object keys are ordered by + UTF-16 code unit rather than locale collation, so a verifier in another language reproduces + the hash without shipping ICU tables. Records without the marker are hashed under the old + locale-dependent order and remain verifiable through a fallback path. +- A rotation manifest, `segments.jsonl`, that binds every sealed segment to its own bytes. Each + entry carries a hash of itself and the final hash of the segment before it, so deleting a + rotated segment, reordering two of them, or rewriting one after it was sealed is now + detectable rather than silently invisible. +- Live-tail re-derivation. A checkpoint commits to a prefix of the live file, so the log growing + after a checkpoint is normal and does not invalidate it, while rewriting any record inside the + committed prefix fails verification. +- Rejection of records containing duplicate JSON keys. Implementations disagree about which + value of a repeated member wins, so a duplicate key is a way to hand two readers the same + bytes and have them reach different conclusions about what was recorded. +- Off-box anchoring wired to the CLI: `agentwall anchor` seals the current segment, signs an + Ed25519 checkpoint over the composite state, and submits it to OpenTimestamps calendars. + `agentwall verify` reports the chained, linked, and anchored layers separately and exits + non-zero unless all three hold. +- An independent verifier, `agentwall-verify`, written in Go against `docs/audit-format.md` + rather than against our source. It uses the Go standard library and nothing else, so + `go list -m all` prints one line, and it performs no network access and writes no files. + Released as static binaries for linux, macOS, and Windows. +- A 26 case conformance corpus covering valid evidence, forgeries, and boundary conditions, run + through both verifiers on every commit. The two agree on 22 cases; the 4 remaining are places + the bundled TypeScript verifier accepts evidence the format rejects, declared explicitly in + the harness rather than papered over. +- `docs/audit-format.md`, the normative specification. The implementations conform to it, not + the other way around. - FloodGuard shield mode control surface in the dashboard. - FloodGuard per-session temporary override API and operator controls. - Forward-facing Agentwall logo assets wired into README and public HTML surfaces. @@ -15,6 +58,10 @@ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.ht - Approval webhook notifications for queued and resolved manual reviews via `approval.webhookUrl`. ### Changed +- The npm package is published as `@reesebuilt/agentwall`. The unscoped name `agentwall` on npm + belongs to an unrelated project and always has. The installed command is still `agentwall`. +- The supported Node floor is 22.12.0, declared in `engines`. Node 20 reached end of life in + April 2026 and a security tool should not advertise a runtime that stops receiving fixes. - CLI terminate now requires `--confirm` so hard containment is deliberate instead of one typo away. - Live-control docs now point to the monitor-first example on port `3015` to avoid false 401/404 debugging on the wrong local service. - Session control CLI errors now explain how to recover from `Session not found` by seeding a live runtime session first. @@ -44,6 +91,21 @@ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.ht - Policy and config YAML now parses under the YAML 1.2 core schema on js-yaml 5. Merge keys (`<<`) are no longer expanded, so a policy file that relies on one is rejected whole and the last good ruleset stays in force instead of a partially assembled rule taking effect. Unquoted dates load as strings rather than `Date` objects, and a mapping with a complex key is rejected instead of having that key flattened into a lossy string. - The dashboard runtime-context panel now degrades to "none" when the agent harness config file cannot be parsed, rather than failing the whole dashboard state build on a file Agentwall does not own. +### Fixed +- OpenTimestamps proof files are named after the checkpoint digest instead of a counter that + only advances on rotation. Two anchor passes without a rotation between them previously wrote + the same filename, so each pass destroyed the proof the previous one had obtained. Anchoring + on a schedule would have erased evidence at every interval. +- `agentwall verify` no longer reports "nothing anchored off-box yet" when the anchor log has + entries. It reached that message whenever the anchor log and the checkpoint key were not both + present, and said it in a state where it was false. + +### Removed +- The direct `pino` dependency, which nothing imported. Fastify owns the logger instance the + server actually uses and depends on pino itself, so the declared dependency only pinned a + second, unused copy of pino 8 in the tree. Runtime dependencies are now three, `fastify`, + `js-yaml`, and `zod`, and a clean install carries 11 fewer transitive packages. + ## [0.1.0] - 2026-03-23 ### Added diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b43b39f..4dc1414 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -44,8 +44,9 @@ worse than no tool. ## Dependencies -Runtime dependencies are deliberately four: `fastify`, `js-yaml`, `pino`, `zod`. Adding a fifth -needs a reason in the pull request. +Runtime dependencies are deliberately three: `fastify`, `js-yaml`, `zod`. Adding a fourth needs +a reason in the pull request. A dependency nothing imports is not a dependency; declare what the +code actually uses. The audit, signing, and anchoring paths use Node's own `crypto` and plain HTTP with no third-party clients. Do not add a dependency there. A supply-chain compromise inside the diff --git a/README.md b/README.md index 7a9a5c6..f4645b0 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ and keeps a record that cannot be quietly rewritten. CI License: Apache-2.0 TypeScript - Node >= 20 + Node 22.12+ Platform: Linux

@@ -53,7 +53,19 @@ The rest of the limits are in [Limits](#limits). They are not footnotes. ## Quick start -Linux, Node.js 20 or newer. Verified on Node 24.14.1. +Linux, Node.js 22.12 or newer. Verified on Node 24.14.1. + +```bash +npm install -g @reesebuilt/agentwall + +agentwall init --mode monitor +agentwall doctor +``` + +The npm package named `agentwall`, without a scope, is a different and unrelated project. This +one is `@reesebuilt/agentwall`; the command it installs is `agentwall`. + +From a checkout instead: ```bash git clone https://github.com/reesebuilt/agentwall.git @@ -634,11 +646,12 @@ Egress observed by the proxy enters the same hash chain, attributed to the origi ## Built with -TypeScript 5 (strict) on Node.js 20+, Fastify 5, Zod, pino, YAML policy via `js-yaml`, Jest. -Runtime dependencies are deliberately four: `fastify`, `js-yaml`, `pino`, `zod`. The audit and -anchoring paths use Node's own `crypto` and plain HTTP with no third-party clients, because a -dependency inside the component whose entire job is being trustworthy is a supply-chain risk -this project declines. +TypeScript 5 (strict) on Node.js 22.12+, Fastify 5, Zod, YAML policy via `js-yaml`, Jest. +Runtime dependencies are deliberately three: `fastify`, `js-yaml`, `zod`. Logging is Fastify's +own pino instance, which arrives as its dependency rather than ours. The audit and anchoring +paths use Node's own `crypto` and plain HTTP with no third-party clients, because a dependency +inside the component whose entire job is being trustworthy is a supply-chain risk this project +declines. ## Docs diff --git a/package-lock.json b/package-lock.json index ea1850c..3b2231e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,16 @@ { - "name": "agentwall", - "version": "0.1.0", + "name": "@reesebuilt/agentwall", + "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "agentwall", - "version": "0.1.0", + "name": "@reesebuilt/agentwall", + "version": "0.2.0", "license": "Apache-2.0", "dependencies": { "fastify": "5.11.0", "js-yaml": "5.2.3", - "pino": "^8.19.0", "zod": "4.4.3" }, "bin": { @@ -19,11 +18,14 @@ }, "devDependencies": { "@jest/globals": "^30.4.1", - "@types/node": "^20.11.5", + "@types/node": "^22.20.1", "jest": "^30.4.2", "ts-jest": "^29.4.12", "ts-node": "^10.9.2", "typescript": "^5.3.3" + }, + "engines": { + "node": ">=22.12.0" } }, "node_modules/@babel/code-frame": { @@ -638,9 +640,9 @@ "license": "MIT" }, "node_modules/@fastify/fast-json-stringify-compiler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.0.3.tgz", - "integrity": "sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.1.0.tgz", + "integrity": "sha512-PxcYtKLbQ8Z+yApiqjK8FwxIwvEj38k2OiLc17u8dkJSlmfi2wHHPaSnaoqBPQqtvF8YVsDgDpP2snDCfFrpfw==", "funding": [ { "type": "github", @@ -653,13 +655,13 @@ ], "license": "MIT", "dependencies": { - "fast-json-stringify": "^6.0.0" + "fast-json-stringify": "^7.0.0" } }, "node_modules/@fastify/forwarded": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-3.0.1.tgz", - "integrity": "sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-3.0.2.tgz", + "integrity": "sha512-NE8HgKLgYejV9lDpqkEFaDKMLYelJBVfHekhB0UKvX0ghagXRJqg68feg8er1NPXxG4N9i6vPxzt8E+3wHfcmA==", "funding": [ { "type": "github", @@ -1362,9 +1364,9 @@ } }, "node_modules/@types/node": { - "version": "20.19.37", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.37.tgz", - "integrity": "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==", + "version": "22.20.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz", + "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==", "dev": true, "license": "MIT", "dependencies": { @@ -1745,18 +1747,6 @@ "win32" ] }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, "node_modules/abstract-logging": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", @@ -1764,9 +1754,9 @@ "license": "MIT" }, "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", "dev": true, "license": "MIT", "bin": { @@ -1790,9 +1780,9 @@ } }, "node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -1917,9 +1907,9 @@ } }, "node_modules/avvio": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/avvio/-/avvio-9.2.0.tgz", - "integrity": "sha512-2t/sy01ArdHHE0vRH5Hsay+RtCZt3dLPji7W7/MMOCEgze5b7SNDC4j5H6FnVgPkI1MTNFGzHdHrVXDDl7QSSQ==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/avvio/-/avvio-9.3.0.tgz", + "integrity": "sha512-g2tQ7LE7oOSqDfwEm3M+ZCMTJc7KiZCdJ4UwyZJb5ckTKyYu50OYmvv0mCFXPuYXoM4zkSt8zM9XQ9KCvxA74A==", "funding": [ { "type": "github", @@ -2042,26 +2032,6 @@ "dev": true, "license": "MIT" }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/baseline-browser-mapping": { "version": "2.11.12", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.12.tgz", @@ -2142,30 +2112,6 @@ "node-int64": "^0.4.0" } }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -2509,9 +2455,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.400", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.400.tgz", - "integrity": "sha512-96EWDNjM59SYflgeV5Ylsf4EMiq1a25YjCnJH7cxn/AF2H3pILRweaUnoLax0yKHWdpOzY6JKEu45e8irqZIHA==", + "version": "1.5.401", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.401.tgz", + "integrity": "sha512-H6ViHN68nGYlChEvlIU67fn8O2/tpbWQPwck98yaJmh+08LSvHiydzDQ6oXNccLU3kNRVIRS9A4mA7CG+i6fLQ==", "dev": true, "license": "ISC" }, @@ -2579,24 +2525,6 @@ "node": ">=4" } }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -2676,9 +2604,9 @@ "license": "MIT" }, "node_modules/fast-json-stringify": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-6.3.0.tgz", - "integrity": "sha512-oRCntNDY/329HJPlmdNLIdogNtt6Vyjb1WuT01Soss3slIdyUp8kAcDU3saQTOquEK8KFVfwIIF7FebxUAu+yA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-7.0.1.tgz", + "integrity": "sha512-eRSayARSbbwlBjpP4vnTTIRD5QPcIrmihPxDeN1DtKnHPg66UuJLx+8hlK1kaFdjvzyQ/dzALoi4vwAQ+T+iZA==", "funding": [ { "type": "github", @@ -2694,11 +2622,27 @@ "@fastify/merge-json-schemas": "^0.2.0", "ajv": "^8.12.0", "ajv-formats": "^3.0.1", - "fast-uri": "^3.0.0", + "fast-uri": "^4.0.0", "json-schema-ref-resolver": "^3.0.0", "rfdc": "^1.2.0" } }, + "node_modules/fast-json-stringify/node_modules/fast-uri": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-4.1.2.tgz", + "integrity": "sha512-TyGmBcbDTZXcb2cj5MV89DrF42DKvb3y5DDUNh95iO+IMeAzMkVSxK1PZRrRIpc9yg8U2GhGdbofNa0LS/a4Bw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fast-querystring": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", @@ -2708,15 +2652,6 @@ "fast-decode-uri-component": "^1.0.1" } }, - "node_modules/fast-redact": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", - "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/fast-uri": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", @@ -2766,117 +2701,6 @@ "toad-cache": "^3.7.0" } }, - "node_modules/fastify/node_modules/fast-json-stringify": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-7.0.1.tgz", - "integrity": "sha512-eRSayARSbbwlBjpP4vnTTIRD5QPcIrmihPxDeN1DtKnHPg66UuJLx+8hlK1kaFdjvzyQ/dzALoi4vwAQ+T+iZA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "@fastify/merge-json-schemas": "^0.2.0", - "ajv": "^8.12.0", - "ajv-formats": "^3.0.1", - "fast-uri": "^4.0.0", - "json-schema-ref-resolver": "^3.0.0", - "rfdc": "^1.2.0" - } - }, - "node_modules/fastify/node_modules/fast-uri": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-4.1.2.tgz", - "integrity": "sha512-TyGmBcbDTZXcb2cj5MV89DrF42DKvb3y5DDUNh95iO+IMeAzMkVSxK1PZRrRIpc9yg8U2GhGdbofNa0LS/a4Bw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fastify/node_modules/pino": { - "version": "9.14.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-9.14.0.tgz", - "integrity": "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==", - "license": "MIT", - "dependencies": { - "@pinojs/redact": "^0.4.0", - "atomic-sleep": "^1.0.0", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "^2.0.0", - "pino-std-serializers": "^7.0.0", - "process-warning": "^5.0.0", - "quick-format-unescaped": "^4.0.3", - "real-require": "^0.2.0", - "safe-stable-stringify": "^2.3.1", - "sonic-boom": "^4.0.1", - "thread-stream": "^3.0.0" - }, - "bin": { - "pino": "bin.js" - } - }, - "node_modules/fastify/node_modules/pino-abstract-transport": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", - "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", - "license": "MIT", - "dependencies": { - "split2": "^4.0.0" - } - }, - "node_modules/fastify/node_modules/pino-std-serializers": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz", - "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==", - "license": "MIT" - }, - "node_modules/fastify/node_modules/process-warning": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", - "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT" - }, - "node_modules/fastify/node_modules/sonic-boom": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz", - "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==", - "license": "MIT", - "dependencies": { - "atomic-sleep": "^1.0.0" - } - }, - "node_modules/fastify/node_modules/thread-stream": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", - "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", - "license": "MIT", - "dependencies": { - "real-require": "^0.2.0" - } - }, "node_modules/fastq": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", @@ -3084,26 +2908,6 @@ "node": ">=10.17.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/import-local": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", @@ -3154,9 +2958,9 @@ "license": "ISC" }, "node_modules/ipaddr.js": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.3.0.tgz", - "integrity": "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.5.0.tgz", + "integrity": "sha512-aq+t5NAc+cS6rZQQVWC2x98CPqGtKKTMDd4Gaodv0wShnItdKg/51djkGJ1hqH+Oy0ivDftCbSLCQob8zso01w==", "license": "MIT", "engines": { "node": ">= 10" @@ -4401,41 +4205,40 @@ } }, "node_modules/pino": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.21.0.tgz", - "integrity": "sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==", + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/pino/-/pino-10.3.1.tgz", + "integrity": "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==", "license": "MIT", "dependencies": { + "@pinojs/redact": "^0.4.0", "atomic-sleep": "^1.0.0", - "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "^1.2.0", - "pino-std-serializers": "^6.0.0", - "process-warning": "^3.0.0", + "pino-abstract-transport": "^3.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", - "sonic-boom": "^3.7.0", - "thread-stream": "^2.6.0" + "sonic-boom": "^4.0.1", + "thread-stream": "^4.0.0" }, "bin": { "pino": "bin.js" } }, "node_modules/pino-abstract-transport": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz", - "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz", + "integrity": "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==", "license": "MIT", "dependencies": { - "readable-stream": "^4.0.0", "split2": "^4.0.0" } }, "node_modules/pino-std-serializers": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", - "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz", + "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==", "license": "MIT" }, "node_modules/pirates": { @@ -4490,19 +4293,20 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/process-warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", - "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.1.0.tgz", + "integrity": "sha512-jQSaVHsPgtyw60e1rQ/A+/ArPEj/S8pS/vFnyGa/gYFXrKk/6RuDkoqVDQ5NI5MmS01698ltlAk0NoDBNLujRw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT" }, "node_modules/pure-rand": { @@ -4544,22 +4348,6 @@ "dev": true, "license": "MIT" }, - "node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/real-require": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", @@ -4636,30 +4424,10 @@ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "license": "MIT" }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/safe-regex2": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.1.0.tgz", - "integrity": "sha512-pNHAuBW7TrcleFHsxBr5QMi/Iyp0ENjUKz7GCcX1UO7cMh+NmVK6HxQckNL1tJp1XAJVjG6B8OKIPqodqj9rtw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.1.1.tgz", + "integrity": "sha512-mOSBvHGDZMuIEZMdOz/aCEYDCv0E7nfcNsIhUF+/P+xC7Hyf3FkvymqgPbg9D1EdSGu+uKbJgy09K/RKKc7kJA==", "funding": [ { "type": "github", @@ -4768,9 +4536,9 @@ } }, "node_modules/sonic-boom": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.1.tgz", - "integrity": "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz", + "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==", "license": "MIT", "dependencies": { "atomic-sleep": "^1.0.0" @@ -4826,15 +4594,6 @@ "node": ">=10" } }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -5100,14 +4859,23 @@ } }, "node_modules/thread-stream": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.7.0.tgz", - "integrity": "sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-4.2.0.tgz", + "integrity": "sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==", "license": "MIT", "dependencies": { - "real-require": "^0.2.0" + "real-require": "^1.0.0" + }, + "engines": { + "node": ">=20" } }, + "node_modules/thread-stream/node_modules/real-require": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-1.0.0.tgz", + "integrity": "sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==", + "license": "MIT" + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -5116,12 +4884,12 @@ "license": "BSD-3-Clause" }, "node_modules/toad-cache": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", - "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.4.tgz", + "integrity": "sha512-m1TdR/rvT7kgGJZhspNtXdsdYk0fddFpJJFlG5s+UkPFo6lkLoZ3YLOaovPYjq1R75NP5JfeTlSHaOsE09peCg==", "license": "MIT", "engines": { - "node": ">=12" + "node": ">=20" } }, "node_modules/ts-jest": { diff --git a/package.json b/package.json index f7c2f8c..fa75c6e 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,18 @@ { - "name": "agentwall", - "version": "0.1.0", + "name": "@reesebuilt/agentwall", + "version": "0.2.0", "description": "Agentwall: provenance-aware policy enforcement for agent egress, tools, content, and approvals", + "homepage": "https://github.com/reesebuilt/agentwall#readme", + "bugs": { + "url": "https://github.com/reesebuilt/agentwall/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/reesebuilt/agentwall.git" + }, "main": "dist/index.js", "scripts": { + "prepack": "npm run build", "build": "tsc", "start": "node dist/index.js", "dev": "ts-node src/index.ts", @@ -27,15 +36,21 @@ "ai-safety" ], "license": "Apache-2.0", + "engines": { + "node": ">=22.12.0" + }, + "publishConfig": { + "access": "public", + "provenance": true + }, "dependencies": { "fastify": "5.11.0", "js-yaml": "5.2.3", - "pino": "^8.19.0", "zod": "4.4.3" }, "devDependencies": { "@jest/globals": "^30.4.1", - "@types/node": "^20.11.5", + "@types/node": "^22.20.1", "jest": "^30.4.2", "ts-jest": "^29.4.12", "ts-node": "^10.9.2", @@ -57,6 +72,7 @@ "files": [ "dist/", "public/", + "!public/assets/*.png", "examples/", "README.md", "LICENSE", diff --git a/verifier/main_test.go b/verifier/main_test.go index e112fd6..8a9977b 100644 --- a/verifier/main_test.go +++ b/verifier/main_test.go @@ -3,6 +3,8 @@ package main import ( "bytes" "encoding/json" + "errors" + "os" "path/filepath" "strings" "testing" @@ -28,6 +30,33 @@ func TestRunVersion(t *testing.T) { } } +// The release stamps the tag into the binary via -ldflags, but `go run ./verifier` from a plain +// checkout reports the compiled-in default instead. That default is a second copy of the +// project's version number, and a second copy drifts: bump package.json, forget report.go, and +// the verifier tells a stranger it is a version that was never released. This test is the only +// thing that notices. +func TestVersionMatchesPackageJSON(t *testing.T) { + raw, err := os.ReadFile(filepath.Join("..", "package.json")) + if errors.Is(err, os.ErrNotExist) { + t.Skip("no package.json beside the verifier; running outside the repo") + } + if err != nil { + t.Fatalf("reading package.json: %v", err) + } + var pkg struct { + Version string `json:"version"` + } + if err := json.Unmarshal(raw, &pkg); err != nil { + t.Fatalf("parsing package.json: %v", err) + } + if pkg.Version == "" { + t.Fatal("package.json has no version field") + } + if pkg.Version != verifierVersion { + t.Fatalf("verifierVersion %q does not match package.json version %q; update verifier/report.go", verifierVersion, pkg.Version) + } +} + func TestRunMissingAuditFileIsIOError(t *testing.T) { var out, errb bytes.Buffer if code := run([]string{"--audit", filepath.Join(t.TempDir(), "nope.jsonl")}, &out, &errb); code != 2 { diff --git a/verifier/report.go b/verifier/report.go index 5717cb0..17a7c71 100644 --- a/verifier/report.go +++ b/verifier/report.go @@ -17,11 +17,19 @@ import ( const ( verifierName = "agentwall-verify" - verifierVersion = "0.2.0" verifierLanguage = "go" verifierCanon = "cu1" ) +// verifierVersion is a var rather than a const so a release build can stamp the tag into the +// binary with -ldflags "-X main.verifierVersion=". The linker refuses to write to a +// const, and it silently ignores a -X naming a symbol that is not a string var, so declaring +// this as a const produces a binary that reports a stale version with no build error to catch +// it: the release would ship a v0.3.0 binary that answers "0.2.0". The default matches the +// version field in package.json, which keeps `go run ./verifier` honest from a plain checkout +// where no linker flags are set. TestVersionMatchesPackageJSON holds the two in step. +var verifierVersion = "0.2.0" + // problem is one finding. code is stable and machine-readable and appears in JSON output; text // is human guidance and is explicitly not part of any cross-implementation contract. fatal // distinguishes a finding that fails its layer from one that is merely reported, such as a torn