Trust new dependency changes before they land.
OriginFence reviews dependency changes in pull requests and tells you whether to trust them before merge.
It looks only at the packages added or changed in a PR, then decides whether to allow, warn, review, or block them using repo policy, waivers, provenance checks, malicious-package intelligence, and upstream registry signals.
It is built for teams that want trust decisions at merge time, in GitHub, with policy and waivers kept in the repo.
Status:
alpha- supported ecosystems:
npm,PyPI - primary surface:
GitHub pull_requestandmerge_groupworkflows - release tags publish an attested
originfence-action-<tag>.tgzbundle built from the tagged source
OriginFence is intentionally narrow:
- GitHub pull-request and merge-queue workflows only
npmandPyPIonly- changed dependency subjects only, not historical inventory debt
- merge-time trust decisions, not dependency updates, vulnerability triage, or package proxying
This repo dogfoods OriginFence on its own pull requests in observe mode via .github/workflows/originfence-self-trial.yml.
For production-style rollout, treat .originfence/*, .github/CODEOWNERS, .github/workflows/, action.yml, and bundle/github-action as sensitive control-plane files.
OriginFence focuses on the merge boundary:
- new or changed packages in a PR
- hard signals like known malicious packages and quarantined upstream states
- policy decisions that stay with the repo
- output that developers can read in the pull request itself
- known malicious packages and upstream quarantine signals
- missing or invalid provenance where policy requires it
- direct URL and VCS dependencies
- manifest and lockfile drift
- unsupported dependency layouts it cannot evaluate exactly
When a package matches malicious-package intelligence, OriginFence is intentionally blunt:
When policy requires provenance, OriginFence shows the missing evidence directly in the PR:
You can start in observe mode so the check stays green while people see the real decision and next action:
If OriginFence cannot evaluate a dependency layout exactly, it says so explicitly instead of pretending partial coverage:
Add a workflow like this to the consumer repository:
name: OriginFence
on:
pull_request:
merge_group:
types:
- checks_requested
permissions:
contents: read
pull-requests: write
issues: write
jobs:
originfence:
runs-on: ubuntu-latest
steps:
- name: Checkout merge candidate
uses: actions/checkout@v6
with:
fetch-depth: 2
- name: Restore OriginFence cache
uses: actions/cache/restore@v5
with:
path: .originfence/cache
key: ${{ runner.os }}-originfence-${{ github.repository }}-${{ github.ref_name || github.ref }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-originfence-${{ github.repository }}-${{ github.ref_name || github.ref }}-
${{ runner.os }}-originfence-${{ github.repository }}-
- name: Run OriginFence
id: originfence
uses: ctrlzmydecisions/originfence@85183e0f338e23444146461bd1e5938ab5e0af85
with:
enforcement-mode: observe
write-job-summary: "true"
pr-comment: "true"
comment-mode: review_and_block
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Upload OriginFence artifacts
if: always()
uses: actions/upload-artifact@v7
with:
name: originfence-report-${{ github.run_id }}-${{ github.job }}
path: |
${{ steps.originfence.outputs.report-path }}
${{ steps.originfence.outputs.summary-path }}
- name: Save OriginFence cache
if: always()
uses: actions/cache/save@v5
with:
path: .originfence/cache
key: ${{ runner.os }}-originfence-${{ github.repository }}-${{ github.ref_name || github.ref }}-${{ github.sha }}Reference examples:
examples/repo/.github/workflows/originfence-required-check.yml.github/workflows/originfence-reusable.yml
For convenience or trial rollouts, you can still use a moving major tag:
- uses: ctrlzmydecisions/originfence@v0For higher-trust deployment, prefer a full commit SHA. GitHub documents a full-length commit SHA as the only immutable way to pin an action version.
If you want PR authors to be unable to weaken the gate in the same change, keep a baseline policy outside PR control and layer repo-local policy on top of it.
Recommended pattern:
- keep a baseline policy in a separate security-owned repository or another path the PR head cannot rewrite
- pass that file through
baseline-policy-path - keep repo-local
.originfence/policy.yamlfor stricter additions only - protect
.originfence/*with.github/CODEOWNERSand branch rules once you have more than one maintainer
Example:
jobs:
originfence:
runs-on: ubuntu-latest
steps:
- name: Checkout merge candidate
uses: actions/checkout@v6
with:
fetch-depth: 2
- name: Checkout security baseline policy
uses: actions/checkout@v6
with:
repository: your-org/security-policy
ref: main
path: .originfence-baseline
- name: Run OriginFence
id: originfence
uses: ctrlzmydecisions/originfence@85183e0f338e23444146461bd1e5938ab5e0af85
with:
baseline-policy-path: .originfence-baseline/originfence/baseline.policy.yaml
policy-path: .originfence/policy.yaml
waivers-path: .originfence/waivers.yaml
enforcement-mode: observe
github-token: ${{ secrets.GITHUB_TOKEN }}OriginFence merges the baseline and repo policy in the stricter direction, so the repo-local policy can tighten the baseline but not weaken it.
GitHub Actions consume committed JavaScript bundles, so OriginFence still ships the Action as prebuilt bundled JavaScript at each tag. The release process now reduces that trust gap in three ways:
- CI fails if
bundle/github-actiondrifts from the TypeScript source - published releases rebuild the Action from the tagged commit before packaging it
- each release uploads an attested
originfence-action-<tag>.tgzbundle plus aSHA256SUMS.txtfile
The attested release bundle is for verification. Consumers still install OriginFence the normal GitHub Action way:
- uses: ctrlzmydecisions/originfence@v0You can verify a release bundle with GitHub CLI:
gh release download v0.1.2-alpha -R ctrlzmydecisions/originfence -p 'originfence-action-*.tgz' -p 'SHA256SUMS.txt'
sha256sum --check SHA256SUMS.txt
gh attestation verify originfence-action-v0.1.2-alpha.tgz -R ctrlzmydecisions/originfence- Compare the base revision to the PR head.
- Resolve only the changed dependency subjects from supported manifests and lockfiles.
- Gather registry metadata, cryptographically verified provenance signals, malicious-package intelligence, and OriginFence drift history.
- Apply baseline policy, repo policy, and explicit waivers.
- Emit a required-check result, job summary, optional sticky PR comment, and canonical JSON report.
Recommended rollout path:
- Add
.originfence/policy.yamland.originfence/waivers.yamlfrom a preset. - Start with
enforcement-mode: observe. - Review job summaries and PR comments for a week or two.
- Keep the cache step in place so drift history and provenance trust roots stay warm across runs.
- Tune policy and waivers.
- Move to a pinned-SHA action reference and a baseline policy outside PR control before relying on it as a blocking gate.
- Switch to
enforcement-mode: enforcewhen the repo is ready.
Preset entry points:
presets/observe.policy.yamlpresets/balanced.policy.yamlpresets/strict.policy.yamlpresets/waivers.yaml
If you are running OriginFence from a local checkout, you can bootstrap config files directly:
npm install
npm run build:test
node dist/src/cli.js init --preset observeOriginFence evaluates malicious-package signals in this order:
- optional local override entries from
malicious-packages-filefor emergency or org-specific blocks - OpenSSF malicious-packages through OSV
- GitHub npm malware advisories as a supplemental npm source
PyPI project status is treated separately as a hard upstream registry signal.
OriginFence keeps HTTP cache, drift snapshots, and provenance trust-root state under .originfence/cache by default. Persist that directory with actions/cache if you want drift signals and provenance verification to stay warm on GitHub-hosted runners.
OriginFence always writes:
- a canonical JSON report
- a plain-text summary
In GitHub, OriginFence maps decisions to required-check-friendly statuses:
success: onlyallowandwarnfailure: anyrevieworblockneutral: no supported dependency changes were detected
In observe mode, OriginFence keeps the underlying decision in the report and human-readable output, but remaps the workflow status to success so repos can tune policy without merge disruption.
- GitHub is the only first-class delivery surface in v1.
- OriginFence supports
npmandPyPIonly. - OriginFence evaluates changed subjects only; it is not a full historical dependency inventory scanner.
- OriginFence is a PR gate, not a transparent package proxy.
- Python support covers common
requirements.txtspecifiers, include files, direct references, and editable VCS entries.uv.lockstill gives OriginFence the strongest exact-resolution path. - Provenance verification is strongest for exact releases. For Python,
uv.lockor tightly pinnedrequirements.txtgives OriginFence the cleanest evidence path.
npm install
npm run build
npm testThe public repo includes the test suite and fixture corpus used by npm test, so contributors can validate behavior locally instead of relying only on screenshots or release notes.
Useful paths:



