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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Follows [Keep a Changelog](https://keepachangelog.com/); versioning is [SemVer](

### Added

- Source provenance policy SSOT + SelfCheck (C06 L59): `docs/ops/source-provenance.md`, `scripts/source-provenance-check.ps1 -SelfCheck`, `branch-protection-check.ps1 -PolicyOnly` hermetic hook, CONTRIBUTING cross-link (signed commits + CODEOWNERS + human org gates; live Settings remain NOT_VERIFIABLE_IN_REPO).

- SLSA L3 environment isolation SelfCheck (C06 L53): `scripts/slsa-isolation-check.ps1 -SelfCheck`, isolated container rebuild evidence row in `hermetic-builds.md`, `repro-check.ps1 -PolicyOnly` isolation hook, soft CI in `hermetic.yml` (not a full L3 attestation).

- ADR 0006: explicit no MCP host/server / pin list (C06 L57) + `mcp-scope` SelfCheck.
Expand Down
17 changes: 17 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,5 +201,22 @@ Verify locally:
pwsh -NoProfile -File scripts/commit-signing-check.ps1 -Ref HEAD -Count 5
```

## Source provenance (signed commits + CODEOWNERS)

SessionLedger's **source provenance** policy SSOT covers cryptographic commit
signatures, [`CODEOWNERS`](CODEOWNERS) review expectations, and human org gates
(branch protection, maintainer 2FA) that cannot be verified from checkout alone.
See [`docs/ops/source-provenance.md`](docs/ops/source-provenance.md).

Hermetic policy smoke (no GitHub API):

```powershell
pwsh -NoProfile -File scripts/source-provenance-check.ps1 -SelfCheck
pwsh -NoProfile -File scripts/branch-protection-check.ps1 -PolicyOnly
```

Live branch protection remains a maintainer Settings control; the scripts above
document anchors and do not claim org Settings are enforced from the tree.

## Governance
This repository follows governance guidelines defined in ~/.claude/CLAUDE.md at a high level.
24 changes: 12 additions & 12 deletions WAVE37_LANE.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
# Wave-37 lane: w37-miri-hardC00 L7 blocking Miri permutation CI
# Wave-37 lane: w37-source-provC06 L59 source-code provenance policy

**Branch:** `feat/sl-w37-miri-hard`
**Worktree:** `C:\Users\koosh\SessionLedger-wtrees\w37-miri-hard`
**Cluster / pillar:** C00 L7
**Wave-36 overlap:** #296 loom permutation blocking
**Branch:** `feat/sl-w37-source-prov`
**Worktree:** `C:\Users\koosh\SessionLedger-wtrees\w37-source-prov`

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The worktree path contains a hardcoded absolute local path specific to a single user's environment. It is recommended to use a relative path or omit the absolute local path entirely to keep the documentation portable and avoid leaking local system details.

Suggested change
**Worktree:** `C:\Users\koosh\SessionLedger-wtrees\w37-source-prov`
**Worktree:** w37-source-prov

**Cluster / pillar:** C06 L59

## Gap

`miri-smoke.yml` is nightly + `continue-on-error`. Add PR-blocking Miri job for
`race_model` (and optional `loom_model` subset) with policy doc anchors.
L59 score 2: signed merges present but no in-repo signed-commit policy SSOT.
Extend branch-protection + contributor policy with machine-verifiable anchors.

## Acceptance criteria

1. Add `scripts/miri-permutation-check.ps1 -SelfCheck` documenting policy.
2. Add `.github/workflows/miri-permutation.yml` blocking on PR/push (ubuntu, race_model).
3. Keep existing `miri-smoke.yml` soft nightly or document split.
4. Update `docs/ops/concurrency-safety.md`.
1. Add `docs/ops/source-provenance.md` SSOT (signed commits, CODEOWNERS, human org gates).
2. Add `scripts/source-provenance-check.ps1 -SelfCheck`.
3. Extend `scripts/branch-protection-check.ps1` policy hooks if needed.
4. Update `CONTRIBUTING.md` cross-link.
5. CHANGELOG bullet. **Do not edit** audit scorecard/traceability files.

## Verify

```powershell
pwsh ./scripts/miri-permutation-check.ps1 -SelfCheck
pwsh ./scripts/source-provenance-check.ps1 -SelfCheck
pwsh ./scripts/branch-protection-check.ps1 -PolicyOnly

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The branch-protection-check.ps1 script does not define a -PolicyOnly parameter. Running this command will result in a parameter binding error. Consider removing the parameter or using -Strict if strict validation is intended.

Suggested change
pwsh ./scripts/branch-protection-check.ps1 -PolicyOnly
pwsh ./scripts/branch-protection-check.ps1

```
14 changes: 14 additions & 0 deletions docs/ops/branch-protection.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,22 @@ unless maintainers pass `-Strict` manually.
without elevated permissions; treat CI as advisory evidence that the script runs,
not as proof that Settings are correct.

## PolicyOnly (hermetic hook)

[`scripts/branch-protection-check.ps1`](../../scripts/branch-protection-check.ps1)
`-PolicyOnly` validates in-repo source-provenance policy anchors (signed commits,
CODEOWNERS, human org gate rows) without calling the GitHub API. Use it in CI or
local smoke when tokens are unavailable:

```powershell
pwsh ./scripts/branch-protection-check.ps1 -PolicyOnly
```

See [`source-provenance.md`](source-provenance.md) (C06 L59 SSOT).

## Related

- [Source provenance policy](source-provenance.md) — signed commits + CODEOWNERS + org gates SSOT
- [Commit signing ops](commit-signing.md) — tip signature check + soft checklist
- [ADR 0004](../adr/0004-commit-signing-policy.md) — signed-commit policy
- [SECURITY.md](../../SECURITY.md) — supply-chain index
108 changes: 108 additions & 0 deletions docs/ops/source-provenance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Source code provenance policy

Status: **C06 L59** — SSOT for **signed commits**, [`CODEOWNERS`](../../CODEOWNERS)
review gates, and **human org controls** (branch protection, maintainer 2FA) that
bind who may change SessionLedger source. This page records policy and
machine-verifiable anchors; it does **not** claim that GitHub org Settings are
provable from a git checkout alone.

Related: [`commit-signing.md`](commit-signing.md), [`branch-protection.md`](branch-protection.md),
[`CONTRIBUTING.md`](../../CONTRIBUTING.md), [`SECURITY.md`](../../SECURITY.md),
[`docs/adr/0004-commit-signing-policy.md`](../adr/0004-commit-signing-policy.md).

## Policy layers

| Layer | Requirement | In-repo evidence |
|-------|-------------|------------------|
| DCO | `Signed-off-by:` on each commit | [`CONTRIBUTING.md`](../../CONTRIBUTING.md) |
| GPG / SSH | Cryptographic signature on each commit reaching `main` | [`commit-signing.md`](commit-signing.md), ADR 0004 |
| CODEOWNERS | Review from listed owners on owned paths | [`CODEOWNERS`](../../CODEOWNERS) |
| Branch discipline | Feature branches + PRs; no direct push to `main` | [`CONTRIBUTING.md`](../../CONTRIBUTING.md) |
| Branch protection | Require signed commits + PR before merge on `main` | Human org gate (see below) |
| Maintainer 2FA | Org/account 2FA for Settings / merge access | Human org gate (see below) |

Contributors must configure `commit.gpgsign` (GPG or SSH) **before** pushing.
DCO sign-off is complementary — it is **not** a substitute for GPG/SSH signatures.

## CODEOWNERS review gates

[`CODEOWNERS`](../../CODEOWNERS) assigns default ownership for the tree and
crate-specific paths. GitHub uses CODEOWNERS to request reviews from listed
handles when matching files change.

Maintainers should enable **Require review from Code Owners** on `main` when the
repository settings allow it. That toggle is a **human org gate** — this
repository documents the expectation but cannot assert the toggle from checkout.

## Signed commits (contributor + maintainer)

Operator setup, tip verification, and CI evidence live in
[`commit-signing.md`](commit-signing.md). Local checks:

```powershell
pwsh ./scripts/commit-signing-check.ps1 -Ref HEAD -Count 5
pwsh ./scripts/branch-protection-check.ps1 -PolicyOnly
```

Branch protection machine-verify (best-effort `gh api`, soft-skip without admin
scope): [`branch-protection.md`](branch-protection.md) +
[`scripts/branch-protection-check.ps1`](../../scripts/branch-protection-check.ps1).

## Human org gates (Settings)

These controls are **required policy** but **not** machine-verifiable from
repository contents:

| Control | Why not in-tree? | Maintainer action |
|---------|------------------|-------------------|
| **Require signed commits** on `main` | GitHub branch protection API needs admin scope | Settings → Branches → `main` |
| **Require a pull request before merging** | Same | Same |
| **Require review from Code Owners** | Same | Same (when CODEOWNERS is active) |
| **Maintainer 2FA / hardware keys** | GitHub does not expose per-user 2FA to this repo | Org/account Settings (human attestation) |

Record the date branch protection was enabled in an internal ops note. Do **not**
commit screenshots of GitHub Settings or 2FA enrollment.

## What this repository can verify

| Control | Verifiable in-tree? | Evidence |
|---------|---------------------|----------|
| Signed-commit policy SSOT | **Yes** | This page + ADR 0004 |
| CODEOWNERS file present | **Yes** | [`CODEOWNERS`](../../CODEOWNERS) |
| Recent `main` tip signature | Partial | `scripts/commit-signing-check.ps1` |
| Branch protection doc anchors | **Yes** | `scripts/branch-protection-check.ps1 -PolicyOnly` |
| GitHub branch protection live state | Partial (best-effort API) | `scripts/branch-protection-check.ps1` (no `-PolicyOnly`) |
| Org 2FA / hardware-key enrollment | **No** | Human attestation only |

## Evidence checklist

| Gate | Status | Evidence / prerequisite |
|------|--------|-------------------------|
| Source provenance policy documented | **done** | This page |
| Source provenance SelfCheck | **done** | `scripts/source-provenance-check.ps1 -SelfCheck` |
| Branch protection PolicyOnly hook | **done** | `scripts/branch-protection-check.ps1 -PolicyOnly` |
| CONTRIBUTING.md cross-link | **done** | [`CONTRIBUTING.md`](../../CONTRIBUTING.md) § Source provenance |
| CODEOWNERS present | **done** | [`CODEOWNERS`](../../CODEOWNERS) |
| GitHub **Require signed commits** live proof | **NOT_VERIFIABLE_IN_REPO** | Human org gate — enable in Settings; optional `gh api` via branch-protection-check without `-PolicyOnly` |
| Maintainer 2FA live proof | **NOT_VERIFIABLE_IN_REPO** | Human org gate — org/account 2FA attestation out-of-band |

## SelfCheck (machine proof)

Docs + path anchors only — no network, no GitHub API, no false org-gate claims:

```powershell
pwsh ./scripts/source-provenance-check.ps1 -SelfCheck
```

The script asserts:

- This page documents **signed commits**, **CODEOWNERS**, and **human org gates**
- Cross-links to `CONTRIBUTING.md`, `commit-signing.md`, and `branch-protection.md`
- The evidence checklist includes **NOT_VERIFIABLE_IN_REPO** human org rows
- `scripts/branch-protection-check.ps1` exposes `-PolicyOnly` for hermetic policy hooks

## Related

- [`branch-protection.md`](branch-protection.md) — PR + signed-commit machine verify
- [`commit-signing.md`](commit-signing.md) — contributor GPG/SSH setup + CI tip check
- [`docs/adr/0004-commit-signing-policy.md`](../adr/0004-commit-signing-policy.md) — decision record
52 changes: 51 additions & 1 deletion scripts/branch-protection-check.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,60 @@ param(
[string]$Repo = "KooshaPari/SessionLedger",
# When set, exit 1 only if the API succeeds and required controls are missing.
# Without -Strict, missing token / 404 / insufficient scope always soft-exit 0.
[switch]$Strict
[switch]$Strict,
# Hermetic policy anchors only (C06 L59 source-provenance hook). No gh / no network.
[switch]$PolicyOnly
)

$ErrorActionPreference = "Stop"
$RepoRoot = Split-Path -Parent $PSScriptRoot

function Assert-SourceProvenancePolicy {
$sourceProvDocPath = Join-Path $RepoRoot "docs/ops/source-provenance.md"
$branchProtectionDocPath = Join-Path $RepoRoot "docs/ops/branch-protection.md"
$codeownersPath = Join-Path $RepoRoot "CODEOWNERS"

foreach ($pair in @(
@{ Path = $sourceProvDocPath; Label = "source provenance policy doc" },
@{ Path = $branchProtectionDocPath; Label = "branch protection doc" },
@{ Path = $codeownersPath; Label = "CODEOWNERS" }
)) {
if (-not (Test-Path -LiteralPath $pair.Path -PathType Leaf)) {
throw "Missing $($pair.Label) at '$($pair.Path)'."
}
}

$sourceProvDoc = Get-Content -LiteralPath $sourceProvDocPath -Raw
$branchProtectionDoc = Get-Content -LiteralPath $branchProtectionDocPath -Raw

if ($sourceProvDoc -notmatch 'Require signed commits') {
throw "docs/ops/source-provenance.md must document Require signed commits."
}
if ($sourceProvDoc -notmatch 'scripts/branch-protection-check\.ps1') {
throw "docs/ops/source-provenance.md must reference scripts/branch-protection-check.ps1."
}
if ($sourceProvDoc -notmatch '-PolicyOnly') {
throw "docs/ops/source-provenance.md must document branch-protection-check.ps1 -PolicyOnly."
}
if ($sourceProvDoc -notmatch 'NOT_VERIFIABLE_IN_REPO') {
throw "docs/ops/source-provenance.md must include NOT_VERIFIABLE_IN_REPO human org rows."
}
if ($branchProtectionDoc -notmatch 'source-provenance\.md') {
throw "docs/ops/branch-protection.md must cross-link docs/ops/source-provenance.md."
}
if ($branchProtectionDoc -notmatch 'Require signed commits') {
throw "docs/ops/branch-protection.md must document Require signed commits."
}

Write-Host "Source provenance policy OK (docs + CODEOWNERS anchors; no live Settings claim)."
}

if ($PolicyOnly) {
Write-Host "Branch protection check (PolicyOnly — C06 L59 hermetic policy hook)"
Assert-SourceProvenancePolicy
Write-Host "PolicyOnly: skipping GitHub API branch protection query."
exit 0
}

function Write-Skip {
param([string]$Reason)
Expand Down
Loading
Loading