Skip to content

Documentation

The CI package gate

Fail a pipeline before it installs a package your policy blocks.

What it does

The gate is one Node script with no dependencies. It reads your lockfile — package-lock.json, npm-shrinkwrap.json or pinned pip requirements — and asks SecureFlow about every package in it. If your organisation’s policy blocks one, the job fails before the install step, so the blocked package’s install script never runs.

What is blocked, warned about or allowed is your policy’s decision, made by SecureFlow; the gate only decides what a refusal does to the job. Every question is recorded, and each run appears under Protect → Packages → Coverage.

Installing it

There is no GitHub Action or package to install. Your pipeline downloads the gate from your own SecureFlow, checks it, and runs it. In SecureFlow, Protect → Packages → Coverage → Set up the CI gate gives the step for GitHub Actions, GitLab CI, Bitbucket Pipelines or a plain shell, already filled in with your workspace id, the API’s address, and the current version with its SHA-256.

Create an API key with the `packages:evaluate` scope in Settings → API keys, and keep it in your CI’s secret store as `SECUREFLOW_API_KEY`. Put the step before anything installs packages. It needs a POSIX shell, curl, sha256sum and Node 20 or later.

The step, in any CI with a shell

#!/bin/sh
# SecureFlow package gate <version>: run it before anything installs packages.
# SECUREFLOW_API_KEY comes from your CI's secret store, never from this file.
set -eu
export SECUREFLOW_API_URL='https://secureflow.ruah.ai'
export SECUREFLOW_WORKSPACE='<your workspace id>'
export SECUREFLOW_LOCKFILES='package-lock.json'
GATE="${TMPDIR:-/tmp}/secureflow-gate.mjs"
curl -fsSL -o "$GATE" "https://secureflow.ruah.ai/v1/public/gate/<version>/gate.mjs"
echo "<its SHA-256>  $GATE" | sha256sum -c -
node "$GATE"

Pinning a version

Every release is published at its own address, https://secureflow.ruah.ai/v1/public/gate/<version>/gate.mjs, and never changes, so a pipeline pinned to one keeps working after the next. There is no "latest" address to download from: a job runs whatever it downloads, with your code and the key in reach.

The step checks the download against the SHA-256 written into it, and stops before running anything if they differ. https://secureflow.ruah.ai/v1/public/gate/releases.json lists every version with its checksum, and `<version>/gate.mjs.sha256` gives one in sha256sum format. Look the value up when you adopt or move to a version and write it into the pipeline: a checksum fetched from the same place on every run would check nothing.

The bundle is checked by its checksum, not signed. The key that signs proofs signs proof records only.

Settings

Each is an environment variable the step sets. Only the key is a secret, and the gate never prints it.

What the gate reads
VariableWhat it isDefault
SECUREFLOW_API_URLYour SecureFlow API. Must be https.Required
SECUREFLOW_API_KEYAn API key with the `packages:evaluate` scope, from the CI’s secret store.Required
SECUREFLOW_WORKSPACEThe workspace whose package policy applies.Required
SECUREFLOW_LOCKFILESLockfiles to read, separated by commas or new lines.package-lock.json
SECUREFLOW_COMPARE_REFEvaluate only packages that are new since this git ref, such as the base branch of a pull request.Everything
SECUREFLOW_INCLUDE_DEVEvaluate devDependencies, which npm ci installs.true
SECUREFLOW_ON_UNREACHABLE`fail` closes the gate when SecureFlow cannot be reached; `proceed` lets the job go on and records what went through unevaluated.fail
SECUREFLOW_TARGET_IDAttribute the installations to a SecureFlow target.None
SECUREFLOW_CONCURRENCYPackages evaluated at once, 1–32.8

What fails the job

A package the policy blocks. An API key that is refused — expired, revoked, or without `packages:evaluate` — because that is configuration, not an outage, and failing open on it would make revoking a key a way to switch the gate off. SecureFlow being unreachable, unless `SECUREFLOW_ON_UNREACHABLE` is `proceed`. Warnings are printed and pass.

Each blocked or warned package is one line in the job log, naming the package and the rule that fired. GitHub shows those lines as annotations on the lockfile.