From c7055de5e286ba6c029582c720aba52316e1bb52 Mon Sep 17 00:00:00 2001 From: AIOSAI Date: Mon, 8 Jun 2026 00:24:21 -0700 Subject: [PATCH 1/4] =?UTF-8?q?docs(playbook):=20sunday=5Fmerge=20?= =?UTF-8?q?=E2=80=94=20bump=20BOTH=20version=20files,=20pre-flight=20versi?= =?UTF-8?q?on=20check,=20clearer=20manual=20tag=20step=20(from=20this=20ru?= =?UTF-8?q?n's=20friction)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flow/templates/playbook_plans/sunday_merge.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/aipass/flow/templates/playbook_plans/sunday_merge.md b/src/aipass/flow/templates/playbook_plans/sunday_merge.md index c8f9fea0..7fbec6bb 100644 --- a/src/aipass/flow/templates/playbook_plans/sunday_merge.md +++ b/src/aipass/flow/templates/playbook_plans/sunday_merge.md @@ -26,7 +26,8 @@ the vectorized trail. Close when done. - [ ] On `dev`, working tree understood: `drone @git status --all` - [ ] Confirm what's shipping this week — scan uncommitted changes + already-pushed dev commits ahead of main: `git rev-list --count main..dev` (read git, raw ok) - [ ] No surprise files (stray `/tmp` artifacts, test pollution, `.recovery`/`.archive` churn). Clean = archive, never delete. -- [ ] Decide: **release tag this week?** (tag = PyPI publish + GitHub Release). If yes, note target version. +- [ ] **Version state check** (informs the bump decision): read the **two** release-tied versions — `grep '^version' pyproject.toml` and `grep __version__ src/aipass/__init__.py` (they should match; if drifted, note it) — and what PyPI already has: `curl -s https://pypi.org/pypi/aipass/json | python3 -c "import sys,json;print(json.load(sys.stdin)['info']['version'])"`. PyPI rejects a duplicate, so the target must be > published. +- [ ] Decide: **release tag this week?** (tag = PyPI publish + GitHub Release). If yes, note target version. (Significance call is the user's — the PATCH-default rule below is guidance, and the actual release history is a useful tie-breaker.) ## 2. Verify, commit, CHANGELOG @@ -56,6 +57,7 @@ The PR gate (verified against `.github/workflows/`): - [ ] **User's call to merge** — confirm GO - [ ] `drone @git merge ` (squash-merge) +- [ ] ⚠️ The merge command **echoes the PR's ORIGINAL opening description** — often stale if the PR accumulated more work after it was opened. Don't trust it as the merge summary; the real contents are `git log main..dev` from before the merge. - [ ] ⚠️ **Verify `dev` SURVIVES the merge** (the #625 scar — empirical, every time): `drone @git branches` → `dev` still present; `git rev-parse dev` resolves ## 6. Post-merge realign @@ -80,10 +82,15 @@ How the release fires (verified `publish.yml`): a `v*` **git tag push** runs bui - GitHub Release notes = the **topmost `## [...]` CHANGELOG block** (awk-extracted). Steps: -- [ ] Bump `pyproject.toml` version per the rule above, **on dev so it rides into the PR** (then main's merge commit carries the right version) +- [ ] Bump the version in **BOTH** files (they must match the tag, or `__version__` ships wrong): `pyproject.toml` `version` **and** `src/aipass/__init__.py` `__version__`. Do it **on dev so it rides into the PR** (then main's merge commit carries the right version). ⚠️ These two drift easily — `__init__.py` is the one that gets forgotten. - [ ] Confirm the CHANGELOG top section is the release notes you want -- [ ] **Push the tag — MANUAL (drone has no `tag` verb):** Patrick, or raw `git tag v ` + `git push origin v` via `!`, on the merged main commit -- [ ] Verify PyPI shows the new version + the GitHub Release appeared +- [ ] After merge + `drone @git sync`, get the **real** merged-main sha: `git rev-parse HEAD`. **Verify the version on that exact commit BEFORE tagging:** `git show HEAD:pyproject.toml | grep '^version'` and `git show HEAD:src/aipass/__init__.py | grep __version__` — both must equal the tag. +- [ ] **Push the tag — MANUAL (drone has no `tag` verb; devpulse can't push tags):** user runs it, via `!` or terminal. **Two SEPARATE lines, paste the real sha (no `<…>` placeholders, no `&&`):** + ``` + git tag v + git push origin v + ``` +- [ ] Verify PyPI shows the new version + the GitHub Release appeared (`curl -s https://pypi.org/pypi/aipass/json | python3 -c "import sys,json;print(json.load(sys.stdin)['info']['version'])"`) - [ ] Record the tag → Run Summary ## 8. Wrap From dab8d2964598123280ffda5a4b05307667815c52 Mon Sep 17 00:00:00 2001 From: AIOSAI Date: Mon, 8 Jun 2026 10:08:43 -0700 Subject: [PATCH 2/4] security(ci): add least-privilege permissions block to e2e-wheel workflow e2e-wheel.yml was the only workflow missing a top-level permissions: block (added during cross-OS work after PR #624 hardened the rest), so it ran with default broad GITHUB_TOKEN scopes -> OpenSSF Scorecard Token-Permissions = 0. Add 'permissions: contents: read' to match the other 7 workflows. CHANGELOG W24 entry. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/e2e-wheel.yml | 5 +++++ CHANGELOG.md | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/.github/workflows/e2e-wheel.yml b/.github/workflows/e2e-wheel.yml index 727027f2..f9190a39 100644 --- a/.github/workflows/e2e-wheel.yml +++ b/.github/workflows/e2e-wheel.yml @@ -23,6 +23,11 @@ on: - "src/**" workflow_dispatch: +# Least-privilege token (Scorecard Token-Permissions). This workflow only +# reads the repo to build + smoke-test the wheel; it needs no write scopes. +permissions: + contents: read + jobs: e2e-wheel: name: e2e-wheel (${{ matrix.os }}) diff --git a/CHANGELOG.md b/CHANGELOG.md index 116b2855..d2c2e72a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,19 @@ and this project uses [Calendar Versioning](https://calver.org/) in the format --- +## [2026.W24] - 2026-06-08 + +### Security + +- **Least-privilege token on the `e2e-wheel` workflow.** `e2e-wheel.yml` was the + one CI workflow missing a top-level `permissions:` block (it was added during + the cross-OS work after PR #624 hardened the others), so it ran with the + default broad `GITHUB_TOKEN` scopes — dropping the OpenSSF Scorecard + Token-Permissions check to 0. Added `permissions: contents: read`; the + workflow only reads the repo to build and smoke-test the wheel. + +--- + ## [2026.W23] - 2026-06-02 ### Fixed From 076110a2fbf42e52123b7182e0d3249d51fab651 Mon Sep 17 00:00:00 2001 From: AIOSAI Date: Mon, 8 Jun 2026 10:19:51 -0700 Subject: [PATCH 3/4] security(release): sign GitHub Release artifacts with Sigstore (keyless) publish.yml github-release job now signs the wheel + sdist via sigstore/gh-action-sigstore-python (pinned v3.3.0 / 04cffa1d), keyless OIDC, and attaches the .sigstore.json bundles to the GitHub Release through the existing dist/* glob. Added id-token: write to the job for OIDC. PyPI uploads were already attested (Trusted Publishing); Scorecard's Signed-Releases check inspects GitHub Releases, which only carried bare wheels -> score 0. .sigstore.json is in Scorecard's recognized signatureExtensions. Verified: action globs ./dist/*.whl ./dist/*.tar.gz (action.py:202), auto-attach gated on release-event (we trigger on push:tags) so we upload via dist/* and set release-signing-artifacts:false. First live proof = next v* tag. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/publish.yml | 12 ++++++++++++ CHANGELOG.md | 7 +++++++ 2 files changed, 19 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f12f9bf5..875357a7 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -41,12 +41,24 @@ jobs: runs-on: ubuntu-latest permissions: contents: write + id-token: write # keyless Sigstore signing (OIDC); no signing key exists steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: dist path: dist/ + - name: Sign artifacts with Sigstore (keyless, OIDC) + # Produces dist/.sigstore.json bundles next to each wheel/sdist. + # The 'gh release create dist/*' step below then attaches them to the + # GitHub Release, which is where Scorecard's Signed-Releases check looks. + # release-signing-artifacts is disabled: the action's own auto-attach only + # fires on a 'release: published' event, but we trigger on 'push: tags', + # so we upload the bundles ourselves via the dist/* glob. + uses: sigstore/gh-action-sigstore-python@04cffa1d795717b140764e8b640de88853c92acc # v3.3.0 + with: + inputs: ./dist/*.tar.gz ./dist/*.whl + release-signing-artifacts: false - name: Extract latest CHANGELOG section run: | # Grab the topmost "## [...]" block from CHANGELOG.md as release notes. diff --git a/CHANGELOG.md b/CHANGELOG.md index d2c2e72a..f83c1f6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,13 @@ and this project uses [Calendar Versioning](https://calver.org/) in the format default broad `GITHUB_TOKEN` scopes — dropping the OpenSSF Scorecard Token-Permissions check to 0. Added `permissions: contents: read`; the workflow only reads the repo to build and smoke-test the wheel. +- **Signed GitHub Releases via Sigstore (keyless).** The release workflow now + signs the built wheel + sdist with `sigstore/gh-action-sigstore-python` + (keyless OIDC — no signing key is generated, stored, or held by anyone) and + attaches the resulting `.sigstore.json` bundles to the GitHub Release. PyPI + uploads were already attested via Trusted Publishing; this extends verifiable + provenance to artifacts pulled from GitHub Releases and satisfies the OpenSSF + Scorecard Signed-Releases check. First proof lands on the next `v*` tag. --- From 2e96ddc302b0da044b1f1192aaf12c398ad077f8 Mon Sep 17 00:00:00 2001 From: AIOSAI Date: Mon, 8 Jun 2026 10:28:48 -0700 Subject: [PATCH 4/4] chore(release): bump version 2.5.1 -> 2.5.2 (security patch) Mid-week patch release for the CI/release security hardening: least-privilege e2e-wheel token + Sigstore-signed GitHub Releases. Bumps both pyproject.toml and src/aipass/__init__.py __version__. Versioning scheme: patch (2.5.x) = small mid-week fixes, minor (2.x.0) = Sunday main-merge batches. Co-Authored-By: Claude Opus 4.8 (1M context) --- pyproject.toml | 2 +- src/aipass/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index fa59439f..faf0d209 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "aipass" -version = "2.5.1" +version = "2.5.2" description = "A local multi-agent framework where your AI agents keep their memory, work together, and never ask you to re-explain context" readme = "README.md" license = "MIT" diff --git a/src/aipass/__init__.py b/src/aipass/__init__.py index ca6fc5c1..dc734538 100644 --- a/src/aipass/__init__.py +++ b/src/aipass/__init__.py @@ -4,4 +4,4 @@ pip install aipass https://github.com/AIOSAI/AIPass """ -__version__ = "2.5.1" +__version__ = "2.5.2"