diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 66e1396..ebb3999 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,2 +1,2 @@
custom:
- - https://www.devector.io/contact
+ - https://devectorio.github.io/contextfence/#assessment
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 3e9f3ff..12df55d 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -14,7 +14,7 @@ body:
attributes:
label: ContextFence version
description: Run `contextfence --version`, or provide the commit SHA.
- placeholder: 0.1.0
+ placeholder: 0.2.0
validations:
required: true
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index f55df57..c6a8a07 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -58,6 +58,18 @@ jobs:
sha256sum "$filename" > "$filename.sha256"
)
+ - name: Stage reviewed release notes
+ env:
+ RELEASE_VERSION: ${{ github.ref_name }}
+ shell: bash
+ run: |
+ notes="docs/releases/${RELEASE_VERSION}.md"
+ [[ -f "$notes" ]] || {
+ echo "::error::Missing reviewed release notes: $notes"
+ exit 1
+ }
+ cp "$notes" release/RELEASE_NOTES.md
+
- name: Smoke-test the packed CLI
shell: bash
run: |
@@ -78,6 +90,7 @@ jobs:
path: |
release/*.tgz
release/*.sha256
+ release/RELEASE_NOTES.md
if-no-files-found: error
retention-days: 7
@@ -150,11 +163,12 @@ jobs:
GH_TOKEN: ${{ github.token }}
TARBALL: ${{ steps.candidate.outputs.tarball }}
CHECKSUM: ${{ steps.candidate.outputs.checksum }}
+ NOTES: release/RELEASE_NOTES.md
run: >-
gh release create "${{ github.ref_name }}"
"$TARBALL"
"$CHECKSUM"
- --generate-notes
+ --notes-file "$NOTES"
--title "ContextFence ${{ github.ref_name }}"
--verify-tag
--repo "${{ github.repository }}"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0ae7bee..0f5e6ca 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,10 +6,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
## [Unreleased]
+## [0.2.0] - 2026-07-18
+
### Added
- Declarative `matrix` block that expands an authorization model (identities, sources, per-source `allow` lists, and canaries) into the full identity × source grid of deterministic probes: a critical deny probe for every unauthorized identity and a positive control for every authorized one. Generated probes compile down to ordinary v1 probes and report against their source line. Includes `examples/contracts/matrix.boundary.yaml` and `examples/contracts/matrix-leak.boundary.yaml`.
- `contextfence generate` command that turns a connector-neutral access manifest (identities, sources, and per-source `allow` lists) into a matrix boundary contract, deriving probe keys and canaries deterministically and emitting environment placeholders for target and identity credentials. Includes `examples/manifests/northstar-access.yaml`.
+- A clearer interactive lab path: one cache-isolation failure by default, scroll-to-result behavior, published npm and GitHub Action quick starts, and an explicit Boundary Baseline implementation offering.
+- `CITATION.cff`, a sitemap, and expanded social metadata for discovery and research use.
+
+### Changed
+
+- The package homepage now leads to the Devector ContextFence product page; funding links lead to the public assessment scope rather than a generic contact endpoint.
## [0.1.0] - 2026-07-11
@@ -33,5 +41,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
- Machine-readable reports minimize request headers and support safe, synthetic reproduction workflows.
- Release automation uses short-lived OIDC credentials instead of a long-lived npm publishing token after the one-time package bootstrap.
-[Unreleased]: https://github.com/devectorio/contextfence/compare/v0.1.0...HEAD
+[Unreleased]: https://github.com/devectorio/contextfence/compare/v0.2.0...HEAD
+[0.2.0]: https://github.com/devectorio/contextfence/compare/v0.1.0...v0.2.0
[0.1.0]: https://github.com/devectorio/contextfence/releases/tag/v0.1.0
diff --git a/CITATION.cff b/CITATION.cff
new file mode 100644
index 0000000..90ce4e3
--- /dev/null
+++ b/CITATION.cff
@@ -0,0 +1,11 @@
+cff-version: 1.2.0
+message: "If ContextFence supports your work, please cite this software."
+type: software
+title: "ContextFence"
+version: 0.2.0
+date-released: 2026-07-18
+authors:
+ - name: "Devector Consulting Ltd"
+repository-code: "https://github.com/devectorio/contextfence"
+url: "https://www.devector.io/labs/contextfence"
+license: Apache-2.0
diff --git a/README.md b/README.md
index 8e18e99..df52c7b 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ ContextFence is an open-source regression harness for proving that people, teams
Write an identity boundary as YAML, run it against an OpenAI-compatible RAG endpoint, and fail CI when a response or citation crosses the line. ContextFence uses deterministic assertions over observable content and source IDs; it does not ask one model to judge another model's safety.
-[Try the synthetic regression lab](https://devectorio.github.io/contextfence/) · [Read the Action guide](https://github.com/devectorio/contextfence/blob/main/docs/github-action.md) · [Explore the architecture](https://github.com/devectorio/contextfence/blob/main/docs/architecture.md)
+[Try the synthetic regression lab](https://devectorio.github.io/contextfence/) · [Read the Action guide](https://github.com/devectorio/contextfence/blob/main/docs/github-action.md) · [Scope a Boundary Baseline](https://github.com/devectorio/contextfence/blob/main/docs/boundary-baseline.md) · [Explore the architecture](https://github.com/devectorio/contextfence/blob/main/docs/architecture.md)

@@ -25,7 +25,7 @@ A final answer can look correctly redacted after the system has already crossed
These failures span identity, ingestion, retrieval, ranking, caching, citations, and generation. Unit-testing any single layer is not enough. ContextFence makes the end-to-end boundary reviewable, repeatable, and portable across CI, scheduled staging checks, and incident reproduction.
-## What is in v0.1.0
+## What is in v0.2.0
| Capability | Status |
| --- | --- |
@@ -38,13 +38,13 @@ These failures span identity, ingestion, retrieval, ranking, caching, citations,
| OpenAI-compatible black-box target adapter | Shipped |
| Pretty, JSON, JUnit, SARIF, and portable HTML reports | Shipped |
| Severity thresholds, bounded concurrency, aggregate probe timeouts, and explicit exit codes | Shipped |
-| Reusable exact-version GitHub Action (`devectorio/contextfence@v0.1.0`) | Shipped |
+| Reusable exact-version GitHub Action (`devectorio/contextfence@v0.2.0`) | Shipped |
| Interactive vulnerable/remediated browser lab | Shipped |
| Non-root production container and GitHub Pages deployment | Shipped |
| Framework-specific evidence adapters and connector fixtures | Roadmap |
| Hosted scheduling, history, alerts, and private control plane | Commercial direction |
-The `contextfence` package is published on npm, the `devectorio/contextfence@v0.1.0` Action resolves against the `v0.1.0` tag, and the demo container is on GHCR. ContextFence is an early release: the v1 contract is versioned, but backward-compatibility guarantees will firm up before `1.0.0`. The [release runbook](https://github.com/devectorio/contextfence/blob/main/docs/releasing.md) records how releases are built and attested.
+The `contextfence` package is published on npm, the `devectorio/contextfence@v0.2.0` Action resolves against the `v0.2.0` tag, and the demo container is on GHCR. ContextFence is an early release: the v1 contract is versioned, but backward-compatibility guarantees will firm up before `1.0.0`. The [release runbook](https://github.com/devectorio/contextfence/blob/main/docs/releasing.md) records how releases are built and attested.
## Quick start
@@ -53,17 +53,17 @@ Requirements: Node.js 22.14 or newer.
Install the CLI, fetch the network-free example suite, and run it:
```bash
-npm install -g contextfence@0.1.0
+npm install -g contextfence@0.2.0
contextfence --version
-curl -fsSLO https://raw.githubusercontent.com/devectorio/contextfence/v0.1.0/examples/contracts/mock.boundary.yaml
+curl -fsSLO https://raw.githubusercontent.com/devectorio/contextfence/v0.2.0/examples/contracts/mock.boundary.yaml
contextfence test mock.boundary.yaml
```
The intentionally vulnerable cache-replay example shows a real failing report:
```bash
-curl -fsSLO https://raw.githubusercontent.com/devectorio/contextfence/v0.1.0/examples/contracts/vulnerable-cache.boundary.yaml
+curl -fsSLO https://raw.githubusercontent.com/devectorio/contextfence/v0.2.0/examples/contracts/vulnerable-cache.boundary.yaml
contextfence test vulnerable-cache.boundary.yaml
# exits 1 after reporting critical boundary violations
```
@@ -239,10 +239,12 @@ contextfence generate [options]
```
```bash
-contextfence generate examples/manifests/northstar-access.yaml --output boundary.yaml
+curl -fsSLO https://raw.githubusercontent.com/devectorio/contextfence/v0.2.0/examples/manifests/northstar-access.yaml
+contextfence generate northstar-access.yaml --adapter mock --output boundary.yaml
+contextfence test boundary.yaml --dry-run
```
-A manifest lists identities and, for each source, an `allow` list of the identities permitted to see it. Probe keys and canaries are derived deterministically when omitted, and the generated contract emits environment placeholders for target and identity credentials so no secret is written to disk. The output is an ordinary boundary contract—review it, seed each canary into a disposable test index, then run it with `contextfence test`. Live connector imports remain a commercial concern, but the manifest they would produce is a stable, portable seam.
+A manifest lists identities and, for each source, an `allow` list of the identities permitted to see it. Probe keys and canaries are derived deterministically when omitted, and the generated contract emits environment placeholders for target and identity credentials so no secret is written to disk. The command above uses `--adapter mock` so a global install can validate the generated contract without a live target. Change the adapter, add target settings, seed each canary into a disposable test index, and review the result before a live `contextfence test`. Live connector imports remain a commercial concern, but the manifest they would produce is a stable, portable seam.
## GitHub Actions
@@ -254,10 +256,10 @@ permissions:
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- - uses: devectorio/contextfence@v0.1.0
+ - uses: devectorio/contextfence@v0.2.0
with:
contract: examples/contracts/mock.boundary.yaml
- version: 0.1.0
+ version: 0.2.0
format: json
output: reports/contextfence.json
```
@@ -294,7 +296,7 @@ Open [http://localhost:5173](http://localhost:5173).
The demo builds as a non-root static container on port `8080` and versioned images are published to GHCR:
```bash
-docker run --rm --read-only --tmpfs /tmp -p 8080:8080 ghcr.io/devectorio/contextfence:0.1.0
+docker run --rm --read-only --tmpfs /tmp -p 8080:8080 ghcr.io/devectorio/contextfence:0.2.0
```
To build the exact source locally instead:
@@ -361,7 +363,7 @@ A sustainable paid layer can coordinate the enterprise work around that open cor
| Repository workflow | Slack/Teams alerts, SSO/SCIM, approvals, and private runners |
| Public examples and docs | Support, implementation help, and managed upgrades |
-[Devector](https://www.devector.io/) can also deliver fixed-scope RAG boundary assessments using the same public contract format: map identities and sources, seed synthetic canaries, reproduce failures, verify remediation, and leave the customer with executable regression coverage.
+[Devector](https://www.devector.io/) can also deliver a fixed-scope [ContextFence Boundary Baseline](https://github.com/devectorio/contextfence/blob/main/docs/boundary-baseline.md) using the same public contract format: map identities and sources, seed synthetic canaries, reproduce failures, verify remediation, and leave the customer with executable regression coverage.
That creates three complementary routes to revenue—hosted developer tooling, enterprise control plane, and expert assessments—without withholding the useful local core.
diff --git a/SUPPORT.md b/SUPPORT.md
index 04b1f2b..6ec41d0 100644
--- a/SUPPORT.md
+++ b/SUPPORT.md
@@ -2,7 +2,7 @@
## Community support
-Use the repository issue forms for reproducible bugs, feature proposals, and new synthetic threat fixtures. Include the ContextFence version, operating system, Node.js version, a minimal redacted contract, the command you ran, and the observed exit code.
+Use GitHub Discussions for questions, implementation patterns, and early design ideas. Use the repository issue forms for reproducible bugs, feature proposals, and new synthetic threat fixtures. Include the ContextFence version, operating system, Node.js version, a minimal redacted contract, the command you ran, and the observed exit code.
Do not attach live credentials, confidential prompts, raw target responses, or sensitive reports. Report suspected vulnerabilities privately through the process in [SECURITY.md](SECURITY.md).
diff --git a/action.yml b/action.yml
index c3648d9..5a281eb 100644
--- a/action.yml
+++ b/action.yml
@@ -46,7 +46,7 @@ inputs:
version:
description: Exact ContextFence npm package version to execute.
required: false
- default: 0.1.0
+ default: 0.2.0
outputs:
report:
diff --git a/docs/architecture.md b/docs/architecture.md
index c3b092a..035e169 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -21,7 +21,7 @@ flowchart LR
Simulator[Synthetic UI simulator] --> Lab[Interactive regression lab]
```
-The CLI and interactive lab share a problem domain, but they have different execution paths in `v0.1.0`:
+The CLI and interactive lab share a problem domain, but they have different execution paths in the current release:
- The CLI loads YAML contracts and invokes mock or OpenAI-compatible targets.
- The browser lab runs typed, deterministic scenarios entirely in the client. It does not receive target credentials and does not call the CLI.
@@ -143,7 +143,7 @@ secret store -> process environment -> contract interpolation -> request headers
target response -> normalized evidence -> findings -> selected reporter -> artifact retention
```
-ContextFence does not persist a central run database in `v0.1.0`. The chosen output file and CI logs are the durable artifacts. The open-source CLI does not need a hosted ContextFence account.
+ContextFence does not persist a central run database. The chosen output file and CI logs are the durable artifacts. The open-source CLI does not need a hosted ContextFence account.
Known configured credentials are redacted, but arbitrary secrets returned by the target cannot be identified reliably. Use synthetic canaries, not real secrets, and configure CI artifact retention deliberately.
diff --git a/docs/boundary-baseline.md b/docs/boundary-baseline.md
new file mode 100644
index 0000000..adf669e
--- /dev/null
+++ b/docs/boundary-baseline.md
@@ -0,0 +1,34 @@
+# ContextFence Boundary Baseline
+
+The ContextFence CLI remains Apache-2.0 open source. A **ContextFence Boundary Baseline** is optional fixed-scope implementation help for teams that need to turn a real RAG permission model into an executable release control.
+
+## Outcome
+
+In one to two weeks, the engagement turns the boundaries that matter most into a small, reviewable CI suite. The team keeps the contracts, canaries, reports, and workflow after the engagement ends.
+
+## Typical scope
+
+1. Map the testable identity × source boundaries, high-risk access changes, and the retrieval/citation evidence the target exposes.
+2. Create disposable identities and synthetic canaries; no production credentials or customer content are needed for the initial work.
+3. Build an initial 20–40 deterministic allow and deny checks using the public ContextFence contract format.
+4. Reproduce the relevant cache, ACL, retrieval, chunking, or citation failure modes in a safe environment.
+5. Add a reviewed CI or scheduled staging gate and hand over JSON, JUnit, SARIF, or HTML evidence.
+6. Run a remediation and handover session with the engineers responsible for the target.
+
+## Deliverables
+
+- an identity × source boundary map;
+- a versioned ContextFence contract and synthetic fixture/canary plan;
+- a CI pull request or equivalent runner configuration;
+- an evidence-backed findings and remediation summary; and
+- an engineer walkthrough for extending the suite as identities and sources change.
+
+The work is a test and implementation engagement, not a security certification, penetration-test authorization, or guarantee that every access path is safe. Live checks are run only against systems the customer owns or is explicitly authorized to test.
+
+## Hosted design partners
+
+Run history, private runners, connector workflows, evidence retention, alerts, and enterprise identity controls are a future hosted direction. There is no hosted ContextFence control plane being sold as available today. Teams that can help shape that direction may discuss a design-partner engagement.
+
+## Enquire safely
+
+Use the [Devector contact form](https://www.devector.io/contact) or email [dev@devector.io](mailto:dev@devector.io?subject=ContextFence%20Boundary%20Baseline). Describe the target architecture and source systems at a high level; do not send credentials, confidential prompts, raw target responses, or customer data through an enquiry form.
diff --git a/docs/deployment.md b/docs/deployment.md
index 5351a05..48c685d 100644
--- a/docs/deployment.md
+++ b/docs/deployment.md
@@ -41,21 +41,14 @@ docker build \
-t contextfence:subpath .
```
-The GHCR workflow is configured to publish multi-architecture images from `main` and semantic-version tags. The first versioned image does not exist until the npm bootstrap and `v0.1.0` tag are complete; build from source in the meantime:
+The GHCR workflow publishes multi-architecture images from `main` and semantic-version tags. Prefer an immutable versioned image rather than `main`:
```bash
-docker build -t contextfence:local .
-docker run --rm -p 8080:8080 contextfence:local
-```
-
-After the first release, consume a versioned image rather than `main`:
-
-```bash
-docker pull ghcr.io/devectorio/contextfence:0.1.0
-docker run --rm -p 8080:8080 ghcr.io/devectorio/contextfence:0.1.0
+docker pull ghcr.io/devectorio/contextfence:0.2.0
+docker run --rm -p 8080:8080 ghcr.io/devectorio/contextfence:0.2.0
```
-GHCR packages are private on first creation in some repository configurations. Make the package public after the first successful push and link it to the repository.
+The public GHCR package is linked to this repository. For reproducible deployments, record the image digest alongside the release and SBOM/provenance artifacts.
## Runtime posture
diff --git a/docs/github-action.md b/docs/github-action.md
index 46810b1..08130f7 100644
--- a/docs/github-action.md
+++ b/docs/github-action.md
@@ -2,7 +2,7 @@
The repository ships a composite Action that installs one exact ContextFence npm version and runs a boundary contract. Keep the Action ref and its `version` input pinned during reviewable releases.
-> **Release status:** the `v0.1.0` tag and the `contextfence@0.1.0` npm package are published, so the versioned examples below work as written. Pin the Action to a tag or commit SHA you have reviewed.
+> **Release status:** the `v0.2.0` tag and the `contextfence@0.2.0` npm package are published, so the versioned examples below work as written. Pin the Action to a tag or commit SHA you have reviewed.
## Pull request workflow with no secrets
@@ -23,10 +23,10 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- name: Validate the deterministic boundary suite
- uses: devectorio/contextfence@v0.1.0
+ uses: devectorio/contextfence@v0.2.0
with:
contract: examples/contracts/mock.boundary.yaml
- version: 0.1.0
+ version: 0.2.0
format: json
output: reports/contextfence.json
fail-on: low
@@ -70,7 +70,7 @@ jobs:
persist-credentials: false
- name: Test the protected staging target
- uses: devectorio/contextfence@v0.1.0
+ uses: devectorio/contextfence@v0.2.0
env:
CONTEXTFENCE_TARGET_URL: https://rag-staging.example.com
CONTEXTFENCE_TARGET_API_KEY: ${{ secrets.CONTEXTFENCE_TARGET_API_KEY }}
@@ -79,7 +79,7 @@ jobs:
with:
contract: boundaries/staging.yaml
target: https://rag-staging.example.com
- version: 0.1.0
+ version: 0.2.0
format: json
output: reports/contextfence.json
fail-on: high
@@ -100,7 +100,7 @@ Protect `rag-staging` with required reviewers, restrict deployment branches to `
| `concurrency` | `4` | Maximum concurrent probes. |
| `dry-run` | `false` | Validate and plan without calling the target. |
| `working-directory` | `.` | Directory beneath `GITHUB_WORKSPACE` in which to run. |
-| `version` | `0.1.0` | Exact npm package version to execute. Dist-tags and ranges are rejected. |
+| `version` | `0.2.0` | Exact npm package version to execute. Dist-tags and ranges are rejected. |
The `report` output is the report's absolute path, or an empty string when file output is disabled.
@@ -119,10 +119,10 @@ steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- name: Run ContextFence
- uses: devectorio/contextfence@v0.1.0
+ uses: devectorio/contextfence@v0.2.0
with:
contract: boundaries/staging.yaml
- version: 0.1.0
+ version: 0.2.0
format: sarif
output: reports/contextfence.sarif
diff --git a/docs/releases/v0.2.0.md b/docs/releases/v0.2.0.md
new file mode 100644
index 0000000..e25592d
--- /dev/null
+++ b/docs/releases/v0.2.0.md
@@ -0,0 +1,22 @@
+# ContextFence v0.2.0
+
+ContextFence turns a real access model into deterministic CI checks that prove restricted RAG context stays restricted.
+
+## Highlights
+
+- **Permission-matrix contracts.** Declare identities, sources, and allow lists once; ContextFence expands them into the full set of positive and critical-deny probes.
+- **`contextfence generate`.** Convert a portable access manifest into a reviewable boundary contract, with deterministic probe keys and synthetic canaries.
+- **A clearer first run.** The interactive lab now begins with one visible cache-isolation failure, and its install and GitHub Action paths use the published package.
+- **Stronger discovery and handoff.** This release adds citation metadata, a sitemap, expanded share metadata, and a public Boundary Baseline implementation scope.
+
+## Start here
+
+```bash
+npm install -g contextfence@0.2.0
+curl -fsSLO https://raw.githubusercontent.com/devectorio/contextfence/v0.2.0/examples/contracts/mock.boundary.yaml
+contextfence test mock.boundary.yaml
+```
+
+The CLI is Apache-2.0 open source and runs locally or in CI. It is not a security certification; validate only systems you own or are authorized to test.
+
+If the project is useful to your team, a GitHub star helps other engineers discover it. Questions and design ideas belong in GitHub Discussions; reproducible defects belong in Issues.
diff --git a/docs/releasing.md b/docs/releasing.md
index b41c92a..2b5c731 100644
--- a/docs/releasing.md
+++ b/docs/releasing.md
@@ -53,7 +53,7 @@ npm trust github contextfence \
--allow-publish
npm trust list contextfence
-npm deprecate contextfence@0.0.0-bootstrap.0 "Bootstrap placeholder; install 0.1.0 or newer."
+npm deprecate contextfence@0.0.0-bootstrap.0 "Bootstrap placeholder; install the current stable release."
```
The repository, workflow filename, environment name, and package `repository.url` are case-sensitive identity claims. A mismatch makes OIDC publication fail. Once trusted publishing succeeds, configure npm publishing access to require two-factor authentication and disallow traditional tokens.
@@ -95,10 +95,13 @@ Create the release from the exact reviewed `main` commit. A signed tag is prefer
git switch main
git pull --ff-only
git status --short
-git tag -s v0.1.0 -m "ContextFence v0.1.0"
-git push origin v0.1.0
+VERSION="$(node -p \"require('./package.json').version\")"
+git tag -s "v$VERSION" -m "ContextFence v$VERSION"
+git push origin "v$VERSION"
```
+Before tagging, add `docs/releases/v.md`. The release preflight requires it and the workflow publishes that reviewed copy as the GitHub Release body; release notes are not generated from pull-request titles.
+
Pushing the tag starts `.github/workflows/release.yml`. The job:
1. Verifies tag, package, Action, and changelog versions.
@@ -107,7 +110,7 @@ Pushing the tag starts `.github/workflows/release.yml`. The job:
4. Installs the tarball into a clean temporary directory and smoke-tests the packed CLI.
5. Produces a SHA-256 checksum and GitHub build-provenance attestation.
6. Publishes to npm with short-lived OIDC credentials and npm provenance.
-7. Creates the GitHub Release only after npm publication succeeds.
+7. Creates the GitHub Release only after npm publication succeeds, using the reviewed release-note file.
The container workflow independently builds `linux/amd64` and `linux/arm64`, attaches SBOM and provenance metadata, and publishes version and major/minor tags to GHCR.
@@ -116,18 +119,29 @@ Never move an existing release tag or reuse a published npm version. Fix the sou
## Verify after publication
```bash
-npm view contextfence@0.1.0 name version repository dist.integrity --json
+VERSION="$(node -p \"require('./package.json').version\")"
+npm view "contextfence@$VERSION" name version repository dist.integrity --json
verify_directory="$(mktemp -d)"
-npm install --prefix "$verify_directory" contextfence@0.1.0
+npm install --prefix "$verify_directory" "contextfence@$VERSION"
"$verify_directory/node_modules/.bin/contextfence" --version
"$verify_directory/node_modules/.bin/contextfence" test examples/contracts/mock.boundary.yaml
-gh release view v0.1.0 --repo devectorio/contextfence
-gh attestation verify contextfence-0.1.0.tgz --repo devectorio/contextfence
+gh release download "v$VERSION" \
+ --repo devectorio/contextfence \
+ --dir "$verify_directory" \
+ --pattern "contextfence-$VERSION.tgz" \
+ --pattern "contextfence-$VERSION.tgz.sha256"
+(
+ cd "$verify_directory"
+ sha256sum -c "contextfence-$VERSION.tgz.sha256"
+)
+gh attestation verify "$verify_directory/contextfence-$VERSION.tgz" \
+ --repo devectorio/contextfence \
+ --signer-workflow devectorio/contextfence/.github/workflows/release.yml
-docker pull ghcr.io/devectorio/contextfence:0.1.0
-docker inspect ghcr.io/devectorio/contextfence:0.1.0
+docker pull "ghcr.io/devectorio/contextfence:$VERSION"
+docker inspect "ghcr.io/devectorio/contextfence:$VERSION"
```
Also confirm the npm provenance link resolves to the tagged public workflow and that the GitHub Pages deployment loads assets beneath `/contextfence/`.
diff --git a/index.html b/index.html
index 12e2113..8218577 100644
--- a/index.html
+++ b/index.html
@@ -8,19 +8,22 @@
+
-
-
+
+
+
+
- ContextFence — RAG permission regression lab
+ ContextFence — RAG authorization regression tests
diff --git a/package.json b/package.json
index 5decf2c..dfaaa26 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "contextfence",
- "version": "0.1.0",
+ "version": "0.2.0",
"type": "module",
"description": "Playwright for RAG permissions — prove restricted context stays restricted.",
"license": "Apache-2.0",
@@ -12,22 +12,26 @@
"bugs": {
"url": "https://github.com/devectorio/contextfence/issues"
},
- "homepage": "https://devectorio.github.io/contextfence/",
+ "homepage": "https://www.devector.io/labs/contextfence",
"keywords": [
"rag",
"llm",
- "security",
- "permissions",
+ "rag-testing",
+ "rag-security",
+ "ai-security",
"authorization",
+ "access-control",
+ "security-testing",
"regression-testing",
- "ai-security",
+ "github-actions",
+ "devsecops",
"cli"
],
"publishConfig": {
"access": "public",
"provenance": true
},
- "funding": "https://www.devector.io",
+ "funding": "https://devectorio.github.io/contextfence/#assessment",
"main": "./dist/package/index.js",
"types": "./dist/package/types/public.d.ts",
"exports": {
@@ -47,6 +51,7 @@
"docs",
"CHANGELOG.md",
"CODE_OF_CONDUCT.md",
+ "CITATION.cff",
"CONTRIBUTING.md",
"GOVERNANCE.md",
"LICENSE",
diff --git a/public/robots.txt b/public/robots.txt
index c2a49f4..659fe42 100644
--- a/public/robots.txt
+++ b/public/robots.txt
@@ -1,2 +1,3 @@
User-agent: *
Allow: /
+Sitemap: https://devectorio.github.io/contextfence/sitemap.xml
diff --git a/public/sitemap.xml b/public/sitemap.xml
new file mode 100644
index 0000000..62331e3
--- /dev/null
+++ b/public/sitemap.xml
@@ -0,0 +1,7 @@
+
+
+
+ https://devectorio.github.io/contextfence/
+ 2026-07-18
+
+
diff --git a/scripts/release-check.mjs b/scripts/release-check.mjs
index 439afc8..b983061 100755
--- a/scripts/release-check.mjs
+++ b/scripts/release-check.mjs
@@ -1,7 +1,7 @@
#!/usr/bin/env node
import { spawnSync } from 'node:child_process'
-import { readFileSync } from 'node:fs'
+import { existsSync, readFileSync } from 'node:fs'
import { resolve } from 'node:path'
import process from 'node:process'
@@ -168,6 +168,8 @@ const packedFiles = new Set(pack.files.map(({ path }) => path))
for (const required of [
'LICENSE',
'README.md',
+ 'CITATION.cff',
+ 'docs/boundary-baseline.md',
'CONTRIBUTING.md',
'CODE_OF_CONDUCT.md',
'GOVERNANCE.md',
@@ -185,6 +187,20 @@ for (const required of [
if (!packedFiles.has(required)) fail(`npm tarball is missing ${required}`)
}
+const citation = readFileSync(resolve(root, 'CITATION.cff'), 'utf8')
+if (!citation.includes(`version: ${version}`)) {
+ fail(`CITATION.cff must declare release version ${version}`)
+}
+
+const releaseNotesPath = `docs/releases/v${version}.md`
+if (!existsSync(resolve(root, releaseNotesPath))) {
+ fail(`Release notes are missing: ${releaseNotesPath}`)
+}
+const releaseNotes = readFileSync(resolve(root, releaseNotesPath), 'utf8')
+if (!releaseNotes.includes(`ContextFence v${version}`)) {
+ fail(`${releaseNotesPath} must identify ContextFence v${version}`)
+}
+
const readme = readFileSync(resolve(root, 'README.md'), 'utf8')
for (const link of [
'https://devectorio.github.io/contextfence/og.svg',
diff --git a/src/App.tsx b/src/App.tsx
index 4bcd89f..491db8a 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -23,7 +23,7 @@ import {
Zap,
} from 'lucide-react'
import { useRef, useState } from 'react'
-import { defaultFaults, demoSystem, safeFaults } from './data'
+import { demoSystem, safeFaults, starterFaults } from './data'
import type {
FaultConfig,
FaultMode,
@@ -79,13 +79,9 @@ const faultSpecs: FaultSpec[] = [
},
]
-const sourceSetup = `git clone https://github.com/devectorio/contextfence.git
-cd contextfence
-corepack enable
-corepack prepare pnpm@11.0.8 --activate
-pnpm install --frozen-lockfile
-pnpm build:package
-node dist/package/cli.js test examples/contracts/mock.boundary.yaml`
+const quickStart = `npm install -g contextfence@0.2.0
+curl -fsSLO https://raw.githubusercontent.com/devectorio/contextfence/v0.2.0/examples/contracts/mock.boundary.yaml
+contextfence test mock.boundary.yaml`
function assertionSubject(assertion: ProbeAssertion) {
if (assertion.kind === 'source') {
@@ -163,14 +159,15 @@ function Trace({ probe }: { probe: ProbeResult }) {
}
function App() {
- const [faults, setFaults] = useState(() => ({ ...defaultFaults }))
- const [executedFaults, setExecutedFaults] = useState(() => ({ ...defaultFaults }))
- const [suite, setSuite] = useState(() => evaluateSuite(demoSystem, defaultFaults))
+ const [faults, setFaults] = useState(() => ({ ...starterFaults }))
+ const [executedFaults, setExecutedFaults] = useState(() => ({ ...starterFaults }))
+ const [suite, setSuite] = useState(() => evaluateSuite(demoSystem, starterFaults))
const [selectedProbeId, setSelectedProbeId] = useState('probe-cache-isolation')
const [running, setRunning] = useState(false)
const [hasRun, setHasRun] = useState(false)
const [toast, setToast] = useState(null)
const runSequence = useRef(0)
+ const resultsRef = useRef(null)
const selectedProbe =
suite.probes.find((probe) => probe.id === selectedProbeId) ??
@@ -178,13 +175,24 @@ function App() {
suite.probes[0]
const enabledFaultCount = faultSpecs.filter((fault) => faults[fault.key]).length
const hasUnrunChanges = faultSpecs.some((fault) => faults[fault.key] !== executedFaults[fault.key])
+ const heroRunLabel = running
+ ? 'Running deterministic checks…'
+ : hasUnrunChanges
+ ? 'Run the updated 8-check demo'
+ : faults['identity-blind-cache']
+ ? 'Watch the cache leak'
+ : 'Run the 8-check demo'
function notify(message: string, tone: ToastTone = 'success') {
setToast({ message, tone })
window.setTimeout(() => setToast(null), 2800)
}
- async function runSuite(nextFaults: FaultConfig = faults, preferredProbeId?: string) {
+ async function runSuite(
+ nextFaults: FaultConfig = faults,
+ preferredProbeId?: string,
+ revealResults = false,
+ ) {
if (running) return
const sequence = ++runSequence.current
setRunning(true)
@@ -199,6 +207,12 @@ function App() {
setSelectedProbeId(preferredProbe?.id ?? firstFailure?.id ?? nextSuite.probes[0].id)
setHasRun(true)
setRunning(false)
+ if (revealResults) {
+ window.requestAnimationFrame(() => {
+ resultsRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' })
+ resultsRef.current?.focus({ preventScroll: true })
+ })
+ }
notify(
nextSuite.violationCount
? `${nextSuite.violationCount} permission checks found a boundary failure`
@@ -218,12 +232,12 @@ function App() {
void runSuite({ ...safeFaults }, 'probe-cache-isolation')
}
- async function copySourceSetup() {
+ async function copyQuickStart() {
try {
- await navigator.clipboard.writeText(sourceSetup)
- notify('Source setup copied')
+ await navigator.clipboard.writeText(quickStart)
+ notify('Published quick start copied')
} catch {
- notify('Copy is unavailable in this browser — use the source link instead')
+ notify('Copy is unavailable in this browser — use the install link instead')
}
}
@@ -243,10 +257,11 @@ function App() {
- Source
+ GitHub
@@ -259,17 +274,22 @@ function App() {
ContextFence turns declared identity × source boundaries into deterministic regression tests. It checks whether a target exposes unauthorized content or source evidence — not just whether a final answer looks redacted.
- This browser-only lab begins with a deliberately broken simulation. Toggle common implementation mistakes, run the same eight checks, then inspect the simulated source, cache, and retrieval path that crossed the boundary.
+ Start with one deliberately broken cache key: Finance primes a result, then Newsroom asks the same question. Run the eight checks, inspect the exact simulated path that crossed the boundary, then add the other failure modes.
-
+
Synthetic data only. No target credentials, prompts, or production systems are involved.
- These switches simulate implementation errors; they do not change a real service. Change one, then rerun the exact same contract to see which boundary moves.
+ Each switch simulates one implementation defect; none changes a real service. The baseline enables only the cache fault, so its Finance-to-Newsroom replay is easy to trace before you add more variables.
{faultSpecs.map((spec) => (
@@ -348,10 +368,10 @@ function App() {
-
+
-
{hasUnrunChanges ? 'Configuration changed — results are from the last run.' : hasRun ? 'Fresh synthetic run' : 'Starting synthetic baseline'}
+
{hasUnrunChanges ? '2. Configuration changed — results are from the last run.' : hasRun ? '2. Fresh synthetic run' : '2. Baseline: one shared-cache fault is enabled'}
Use the production runner in CI, against systems you are authorized to test.
+
Install the published runner
+
Put an identity × source contract in CI today.
+
+ This page is a synthetic explainer. The Apache-2.0 runner is published on npm, executes versioned contracts against systems you are authorized to test, and produces JSON, JUnit, SARIF, and HTML evidence. Start with the network-free mock suite, then use the pinned GitHub Action for a real staging boundary.
+
- This page is a synthetic explainer. The open-source runner executes versioned contracts, evaluates response content and source metadata exposed by the target, and produces JSON, JUnit, SARIF, and HTML reports. The first npm release is being prepared; until it is published, run it directly from source.
+ Keep the open-source runner either way. For teams with a live RAG system, Devector can deliver a ContextFence Boundary Baseline: a fixed-scope, one-to-two-week engagement that leaves behind an identity × source map, synthetic canaries, executable checks, and evidence your engineers can rerun.
Design-partner track: run history, private runners, connector workflows, and evidence retention are a future hosted direction—not a service being claimed today.
-
{sourceSetup}
+
+
01
Map the boundary
Identify test identities, sensitive sources, access changes, and the evidence the target can expose.
+
02
Prove the failure modes
Seed safe canaries and build an initial 20–40 deterministic checks around the paths that matter.
+
03
Leave with a gate
Hand over a reviewed CI change, portable evidence report, remediation priorities, and an engineer walkthrough.