Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@ env:
CARGO_TERM_COLOR: always

jobs:
# THREAT[TM-RELEASE-001]: Only publishing jobs exchange OIDC for short-lived credentials.
publish-fetchkit:
name: Publish fetchkit
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
Expand Down Expand Up @@ -52,14 +56,21 @@ jobs:
fi
echo "Version verified: $TAG_VERSION"

- name: Authenticate with crates.io
id: auth
uses: rust-lang/crates-io-auth-action@c6f97d42243bad5fab37ca0427f495c86d5b1a18 # v1.0.5

- name: Publish fetchkit to crates.io
run: cargo publish -p fetchkit --locked
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}

publish-fetchkit-cli:
name: Publish fetchkit-cli
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
needs: publish-fetchkit
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
Expand All @@ -73,7 +84,11 @@ jobs:
- name: Wait for crates.io index update
run: sleep 30

- name: Authenticate with crates.io
id: auth
uses: rust-lang/crates-io-auth-action@c6f97d42243bad5fab37ca0427f495c86d5b1a18 # v1.0.5

- name: Publish fetchkit-cli to crates.io
run: cargo publish -p fetchkit-cli --locked
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ Workflows:
- `.github/workflows/publish.yml` - Publishes to crates.io on GitHub Release or manual dispatch

Requirements:
- `CARGO_REGISTRY_TOKEN` secret must be configured in repo settings
- Configure crates.io Trusted Publishing for both crates: GitHub owner `everruns`, repository `fetchkit`, workflow `publish.yml`, no environment. Publishing jobs exchange OIDC for short-lived tokens; no repository publisher secret is used.

Note: `fetchkit-python` is not published to crates.io (`publish = false`). Python release automation is not configured in this repo yet.

Expand Down
4 changes: 4 additions & 0 deletions knowledge/log.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Fetchkit Knowledge Update Log

## 2026-10-25

* **Publishing authentication**: Bind both Rust crates to GitHub OIDC in `publish.yml`; publishing jobs use short-lived tokens from a pinned official auth action, with no legacy secret fallback. Document recovery from current main with the original immutable release source tag and actual registry consumer verification.

## 2026-10-23

* **Release 0.6.0**: Replace the temporary Markkit git override with registry minimum 0.1.1; require Tokio 1.54.1 for RUSTSEC-2026-0386. Publish both Rust crates with `--locked`. The minor bump reflects public `FetchOptions` changes already accumulated since 0.5.0; see the changelog migration notes.
Expand Down
30 changes: 29 additions & 1 deletion knowledge/operations/release-process.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,37 @@ cargo build --workspace --exclude fetchkit-python --release

### Authentication

- GitHub Actions publishing requires `CARGO_REGISTRY_TOKEN`
- Both published crates have crates.io GitHub Trusted Publishers bound to owner
`everruns`, repository `fetchkit`, workflow `publish.yml`, without an environment.
- Only the two publishing jobs have `id-token: write`; both retain `contents: read`.
The pinned `rust-lang/crates-io-auth-action` exchanges their OIDC identities for
short-lived credentials and revokes them when the jobs finish.
- `CARGO_REGISTRY_TOKEN` is populated from the auth step's output, not from a
repository secret. Existing legacy secrets are unused; this change does not
revoke or replace credentials or require trusted-publishing-only enforcement.
- Publisher bindings authorize this workflow persistently. Repository maintainers
who can change/run it are part of the publishing trust boundary.
- Manual release preparation may require GitHub CLI auth for PR creation and merge steps

### Retrying an Existing Release

Before retrying, check both crates.io versions and the immutable release tag SHA.
Never move the tag or increment the version to work around an authentication failure.
If only the authentication workflow changed after tagging, dispatch the workflow
from `main` while retaining the original source tag:

```bash
gh workflow run publish.yml --ref main -f release_tag=v0.6.0
```

Both publishing jobs check out the supplied release tag. Running the workflow at
an old tag instead would also select that tag's old authentication implementation.
After successful upload, verify registry version/checksum/source revision, a fresh
library consumer, and `cargo install fetchkit-cli --version '=0.6.0' --locked`.
A GitHub Release or successful package build alone does not prove publication.
If one crate is already published, do not rerun its upload blindly; resume only the
remaining failed publishing job after checking the registry state.

### Operator Responsibilities

- Human reviews and merges the release PR
Expand Down
20 changes: 20 additions & 0 deletions knowledge/security/threat-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Verified in this review:
| TM-LEAK | Information Leakage | Error messages, metadata exposure, timing |
| TM-CONV | Content Conversion | HTML parsing abuse, injection via converted content |
| TM-AUTH | Bot Authentication | Signing key exposure, replay, signature scope |
| TM-RELEASE | Release Authentication | Publisher credential leakage and workflow identity |

### Managing Threat IDs

Expand Down Expand Up @@ -542,6 +543,25 @@ None — all previously open threats have been mitigated.
| Authority-scoped signatures | TM-AUTH | Signature covers `@authority`; per-host binding |
| Graceful signing failure | TM-AUTH | Signing errors logged, request proceeds unsigned |

## Release Authentication (TM-RELEASE-001)

**Threat**: A leaked long-lived CI publisher secret permits unauthorized crate
publication outside the intended repository workflow.

**Mitigation**: Both crates trust only GitHub repository `everruns/fetchkit`, workflow
`publish.yml`, without an environment. Only its two publish jobs request an OIDC
identity, exchanged by a pinned official action for a short-lived crates.io token.
The action revokes that token when the job completes; the old repository secret
is not read. Publishing retains the release-tag source checkout and `--locked`.

**Trust boundary**: Maintainers able to modify/run that workflow retain publishing
authority. Existing crate ownership, API-token policy, and credentials are unchanged;
trusted-publishing-only enforcement is not enabled by this change.

**Verification**: Workflow lint, exact-head CI, registry metadata/source checks, and
consumer/CLI installation after publication. An auth failure must stop publication;
there is no fallback to an unrelated credential.

## References

- [Fetchkit Tool Contract](../foundations/tool-contract.md) — public tool behavior and security controls
Expand Down
Loading