pf-bridge — keep project files in sync
pf-bridge keeps every project file in sync with your projectfile document. A
projectfile is the single source of truth for a project — identity, people,
links, and tool config — and pf-bridge copies that data out into the files
each tool already reads (README.md, package.json, CITATION.cff, forge
settings) and reads two-way files back in.
| Step | Tool | What it does |
|---|---|---|
| 1. Read / write | pf-cli | get, set, add, del, validate, convert |
| 2. Sync files | pf-bridge (this page) | package.json, CITATION.cff, README.md, … |
| 3. Lower to CI | pf-ci | org.projectfile.ci → workflow YAML |
Installation
Section titled “Installation”Release binary
Latest release 4.3.0. Published for 5 platforms: macOS, Linux.
base=https://kiota.ch/projectfile/bridge/releases/download/v4.3.0curl --fail --location --output ~/.local/bin/pf-bridge \ "$base/pf-bridge-$(uname -s | tr A-Z a-z)-$(uname -m)"chmod +x ~/.local/bin/pf-bridge| Platform | Asset |
|---|---|
| macOS / ARM64 | pf-bridge-darwin-arm64 |
| macOS / x86-64 | pf-bridge-darwin-x86_64 |
base=https://kiota.ch/projectfile/bridge/releases/download/v4.3.0curl --fail --location --output ~/.local/bin/pf-bridge \ "$base/pf-bridge-$(uname -s | tr A-Z a-z)-$(uname -m)"chmod +x ~/.local/bin/pf-bridge| Platform | Asset |
|---|---|
| Linux / ARM64 | pf-bridge-linux-aarch64 |
| Linux / RISC-V | pf-bridge-linux-riscv64 |
| Linux / x86-64 | pf-bridge-linux-x86_64 |
Verify what you downloaded
Every binary ships a detached signature beside it. Confirm it with the maintainer's release key, which the keyserver serves:
gpg --keyserver hkps://keys.openpgp.org --recv-keys 8F89CBF7C8D51F8BEC7533C00925F8F79EBD3257curl --fail --location --output pf-bridge.asc \ "https://kiota.ch/projectfile/bridge/releases/download/v4.3.0/pf-bridge-$(uname -s | tr A-Z a-z)-$(uname -m).asc"gpg --verify pf-bridge.asc ~/.local/bin/pf-bridgeContainer image
Run the tool without installing it. The alias makes every command on this site work unchanged against your working directory:
ghcr.io/projectfile-org/bridge — linux/amd64, linux/arm64, linux/riscv64
docker pull ghcr.io/projectfile-org/bridge:4.3.0alias pf-bridge='docker run --rm --user "$(id -u):$(id -g)" --group-add 0 \ --volume "$PWD:/app/ws" --workdir /app/ws ghcr.io/projectfile-org/bridge:4.3.0 pf-bridge'damianbuho/projectfile-bridge — linux/amd64
docker pull damianbuho/projectfile-bridge:4.3.0alias pf-bridge='docker run --rm --user "$(id -u):$(id -g)" --group-add 0 \ --volume "$PWD:/app/ws" --workdir /app/ws damianbuho/projectfile-bridge:4.3.0 pf-bridge'kiota.ch/projectfile/bridge — linux/amd64
docker pull kiota.ch/projectfile/bridge:4.3.0alias pf-bridge='docker run --rm --user "$(id -u):$(id -g)" --group-add 0 \ --volume "$PWD:/app/ws" --workdir /app/ws kiota.ch/projectfile/bridge:4.3.0 pf-bridge'Stable releases also publish X.Y.Z, X.Y and X tags — pull the precision you want to pin.
Uninstall
rm -f ~/.local/bin/pf-bridge # release binarydocker image rm ghcr.io/projectfile-org/bridge # container imagepf-bridge <bridge> [args] run one bridge (e.g. readme, npm, forge)pf-bridge all [flags] update every file from the projectfile [rw]pf-bridge check [flags] verify declared files match, write nothing [ro]pf-bridge to all write projectfile out to every file [rw]pf-bridge from all read every two-way file back in [rw]pf-bridge --list list installed bridgespf-bridge completion SHELL print completion script (bash|zsh|fish)[rw] updates files, [ro] only reads or compares. check without names
covers the bridges this project declares; check <name>… narrows it, and
check --all covers every installed bridge instead.
Direction is a positional preposition. pf-bridge <name> to writes
projectfile out to the external file and is authoritative; pf-bridge <name> from reads the external file back in; without either, the sync converges —
projectfile values fill and overwrite, and fields projectfile lacks keep the
file’s value. Derive-only bridges always resolve to to.
pf-bridge readme # converge — neither side is clearedpf-bridge readme to # write — projectfile winspf-bridge npm from # read — package.json winspf-bridge all # update every declared filepf-bridge check --all # report drift, write nothingBridges
Section titled “Bridges”Two-way bridges ([rw]) round-trip: the newer side is authoritative and the
other side gap-fills. Derive-only bridges ([ro]) are written from the
projectfile and never read back. The table below is pf-bridge --list itself,
so an installed bridge reaches the page without a hand edit.
| Bridge | What it does | Mode |
|---|---|---|
cache | Manage the local SPDX and include cache | command |
forge | Push projectfile metadata out to the repository forge | command |
init | Scaffold a new projectfile document (prefer pf-cli init) | command |
release-notes | Render release notes from git | command |
scan | Run scanners to refresh projectfile metadata from filesystem signals | command |
composer | composer.json — two-way sync | two-way |
npm | package.json — two-way sync | two-way |
pyproject | pyproject.toml — two-way sync | two-way |
shard | shard.yml — two-way sync | two-way |
cff | CITATION.cff — two-way sync | two-way |
codeowners | CODEOWNERS — two-way sync | two-way |
funding | FUNDING.yml — one-way render | one-way |
fundingjson | funding.json — one-way render | one-way |
ai-policy | AI_POLICY.md — one-way render | one-way |
coc | CODE_OF_CONDUCT.md — one-way render | one-way |
contributing | CONTRIBUTING.md — one-way render | one-way |
dei | DEI.md — one-way render | one-way |
fragments | FEATURES.md, ROADMAP.md, … assembled from docs/<name>.d fragments (one-way render) | one-way |
readme | README.md — one-way render | one-way |
security | SECURITY.md — one-way render | one-way |
support | SUPPORT.md — one-way render | one-way |
browserslist | .browserslistrc — one-way render | one-way |
gitattributes | .gitattributes — one-way render | one-way |
ignore | .claudeignore, .containerignore, .dockerignore, .fdignore, .gitignore, .npmignore, .textlintignore — one-way render | one-way |
license | LICENSE + LICENSES/<spdx>.txt (one-way render, always overwrite) | one-way |
releaserc | .releaserc.yaml — one-way render | one-way |
vulnerabilities | .grype.yaml, .trivyignore, audit-ci.jsonc, osv-scanner.toml — one-way render | one-way |
yamllint | .yamllint — one-way render | one-way |
28 commands — the listing pf-bridge v4.3.0 reported when the page was built.
fragments assembles its files from docs/<name>.d fragments rather than a
single template. ignore covers .claudeignore, .containerignore,
.dockerignore, .fdignore, .gitignore, .npmignore and .textlintignore.
Shared flags
Section titled “Shared flags”Every bridge accepts these, and every one defaults from its PF_BRIDGE_*
environment variable:
| Flag | Description |
|---|---|
--check |
report drift instead of writing — the sync gate; implies --dry-run |
--dry-run |
show what would change without writing |
--fail-on-drift |
under --check, exit non-zero on drift instead of warning |
--force |
overwrite even if the file lacks the marker |
--create-all |
create target files even if they do not exist |
--no-diff |
under --check, report drift without the unified diff |
--preview |
print the rendered file(s) to stdout instead of writing (renderers) |
--offline |
refuse all network fetches; use embedded and cached data only |
--fail-on |
abort includes at error (default) or warning severity |
--ignore-user-config |
run as if no personal config existed |
--sorted |
write YAML keys in sorted order; disables canvas round-trip preservation |
--timeout <dur> |
per-attempt network timeout; retries and backoff are built in (10s) |
-q, --quiet |
suppress info and decision-trace output |
-v, --verbose |
show operational log lines (file detection, includes, locks) |
Subcommands that are not bridges:
Run scanners to refresh projectfile metadata from filesystem signals. Without arguments it opens an interactive picker.
pf-bridge scan [scanner...] [directory] [flags]| Scanner | What it does |
|---|---|
all |
run every registered scanner |
git |
authors, remotes and dates — expands to git-authors, git-remotes, git-dates |
git-authors |
extract authors from git history |
git-remotes |
extract origin URL from git |
git-dates |
extract the root-commit date from git |
stacks |
walk the tree, apply the built-in marker rules, merge tags into [technologies] |
forge |
materialise org.projectfile.forge templates into links[] and repositories[] |
sinks |
derive one package-registry link per public registry in org.projectfile.sinks |
Stack-specific flags only apply when stacks is in the scanner list:
--check (warn about tags with no filesystem evidence, read-only), --strict
(same, but stale tags exit non-zero), --prune (remove stale tags in place).
pf-bridge scan git stackspf-bridge scan stacks --checkpf-bridge scan stacks --prunePush projectfile metadata out to the repository forge, for every non-archive
entry in repositories[].
pf-bridge forge push [directory]pf-bridge forge list [directory] # repositories and whether a token is availableSupported forges: GitHub, GitLab, Forgejo/Gitea. Tokens are read from
GITHUB_TOKEN, GITLAB_TOKEN, FORGEJO_TOKEN, or PF_FORGE_TOKEN_<HOST> for
self-hosted instances. Self-hosted instances on bare hostnames are not
auto-detected; declare them under org.projectfile.forge.kinds:
[org.projectfile.forge.kinds]"code.example.com" = "forgejo"release-notes
Section titled “release-notes”Render release notes from git.
pf-bridge release-notes renderpf-bridge release-notes publishScaffold a new projectfile document with full detection from sources
(package.json, CITATION.cff, …) plus scanner gap-fill. pf-cli init
delegates here, so prefer that entry point.
Keep a local copy of SPDX license texts and HTTP includes, so LICENSE renders
and projectfile reads work offline once warmed. Shared with pf-cli and
pf-ci — a purge here clears it for all three.
pf-bridge cache warmpf-bridge cache statuspf-bridge cache purgeUsing check as a CI gate
Section titled “Using check as a CI gate”pf-bridge check compares what the declared files would become against what is
on disk and writes nothing. Drift warns by default; --fail-on-drift makes it
fatal, which is the form a pipeline wants.
pf-bridge check --all --fail-on-drift