Skip to content

feat: per-ref policies on REST endpoints across all SDKs#30

Open
unknwon wants to merge 3 commits into
mainfrom
jc/ref-policies
Open

feat: per-ref policies on REST endpoints across all SDKs#30
unknwon wants to merge 3 commits into
mainfrom
jc/ref-policies

Conversation

@unknwon
Copy link
Copy Markdown
Contributor

@unknwon unknwon commented May 22, 2026

Summary

  • Extends the optional ops/refs JWT policy claims to every ref-mutating REST method in all three SDKs (Go, TypeScript, Python). Previously only remote-URL token minting accepted ref policies — REST methods like createBranch, merge, createCommit, notes, etc. silently dropped them despite the gateway enforcing the same claim on those endpoints.
  • Adds OP_NO_PUSH / OpNoPush constant alongside the existing OP_NO_FORCE_PUSH.
  • Fixes a pre-existing inconsistency in RepoImpl.create_branch (Python) where policy-denied responses were swallowed into a generic \"HTTP 409\" message; it now falls back to the error key like create_tag/delete_tag/delete_branch already did.
  • Bumps minor versions: Go 0.8.00.9.0, TS 1.8.01.9.0, Python 1.9.01.10.0.

End-to-end validation against acme staging

Verified by minting tokens via each SDK and exercising the gateway directly:

Test Go TypeScript Python
JWT refs claim correctly encoded
create_branch feature/blocked denied by refs/heads/feature/*=no-push
create_tag v-blocked denied by refs/tags/*=no-push
create_branch topic/allowed (non-matching deny) succeeds
create_branch release/v1 first-match-wins (explicit allow before *=no-push)

Test plan

  • Go: go build ./... && go test ./...
  • TypeScript: pnpm run build && pnpm vitest run (178 tests passing)
  • Python: pytest tests/ (159 tests passing)
  • End-to-end staging tests against acme tenant — all SDKs PASS

Extend the optional `ops`/`refs` JWT policy claims to every ref-mutating
operation in the Go, TypeScript, and Python SDKs. Previously only the
remote-URL token minting accepted ref policies; REST methods like
create_branch, merge, commit, and notes silently dropped them.

Adds `OP_NO_PUSH` / `OpNoPush` constant alongside the existing
`OP_NO_FORCE_PUSH`, fixes a pre-existing inconsistency in the Python
SDK where create_branch error responses didn't fall back to the `error`
key, and verifies end-to-end against the acme staging tenant.

Bumps: Go 0.8.0->0.9.0, TS 1.8.0->1.9.0, Python 1.9.0->1.10.0.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR extends JWT policy support so per-ref policy rules (refs claim) are carried through to ref-mutating REST endpoints across the Go, TypeScript, and Python SDKs (not just remote URL minting), adds a no-push policy constant in each SDK, updates docs, and bumps SDK versions accordingly.

Changes:

  • Add refs (per-ref, ordered “first match wins”) policy support and propagate ops/refs through ref-mutating REST calls in Go/TS/Python SDKs.
  • Add no-push policy constant (OP_NO_PUSH / OpNoPush) and helpers to encode refs into the JWT claim shape.
  • Update skill/docs and bump package versions; add/extend unit tests plus optional live smoke scripts.

Reviewed changes

Copilot reviewed 25 out of 25 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
skills/code-storage/SKILL.md Documents repo-wide ops vs per-ref refs, adds no-push, and examples across SDKs.
packages/code-storage-typescript/tests/ref-policies-live.mjs Adds a local-stack live smoke test script for ref policy enforcement.
packages/code-storage-typescript/tests/index.test.ts Adds unit coverage asserting refs is encoded into JWTs and omitted when absent.
packages/code-storage-typescript/src/types.ts Introduces OP_NO_PUSH, PolicyOptions, and per-ref policy types; threads options into ref-mutating APIs.
packages/code-storage-typescript/src/jwt_claims.ts Adds helper to encode RefPolicies into the JWT refs claim tuple format.
packages/code-storage-typescript/src/index.ts Propagates ops/refs into JWT minting for ref-mutating REST methods; emits refs claim when present.
packages/code-storage-typescript/package.json Bumps TypeScript SDK version to 1.9.0.
packages/code-storage-python/tests/test_client.py Adds JWT generation tests for refs claim inclusion/omission.
packages/code-storage-python/tests/ref_policies_live.py Adds a local-stack live smoke test script for ref policy enforcement.
packages/code-storage-python/pyproject.toml Bumps Python SDK version to 1.10.0.
packages/code-storage-python/pierre_storage/version.py Bumps Python SDK internal version constant.
packages/code-storage-python/pierre_storage/types.py Adds OP_NO_PUSH and typed structures for per-ref policy rules.
packages/code-storage-python/pierre_storage/repo.py Threads ops/refs into ref-mutating methods and improves create_branch error message extraction.
packages/code-storage-python/pierre_storage/client.py Plumbs refs through internal JWT generation path.
packages/code-storage-python/pierre_storage/auth.py Adds encode_refs_claim and includes refs in JWT payload when provided.
packages/code-storage-python/pierre_storage/init.py Exposes new constants/types and encode_refs_claim in the public package API.
packages/code-storage-go/version.go Bumps Go SDK version to 0.9.0.
packages/code-storage-go/types.go Adds OpNoPush and ref policy types; extends option structs to carry Ops/Refs.
packages/code-storage-go/repo.go Propagates Ops/Refs into JWT generation for ref-mutating repo methods.
packages/code-storage-go/repo_test.go Adds unit coverage asserting refs claim inclusion/omission in minted JWTs.
packages/code-storage-go/jwt_claims.go Adds helper to encode ref policies into JWT refs claim shape.
packages/code-storage-go/jwt_claims_test.go Adds unit coverage for encodeRefsClaim encoding behavior.
packages/code-storage-go/diff_commit.go Propagates Ops/Refs into diff-commit JWT generation.
packages/code-storage-go/commit.go Propagates Ops/Refs into commit builder JWT generation.
packages/code-storage-go/client.go Emits refs claim when present during JWT generation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +6 to +12
for i, rule := range refs {
opList := []string(rule.Ops)
if opList == nil {
opList = []string{}
}
out[i] = []any{rule.Pattern, opList}
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not a bug. Op is a Go type alias (type Op = string), not a defined type, so Ops is literally []string and []string(rule.Ops) is a no-op conversion that compiles fine. Verified by go build ./... && go test ./... green on this branch.

t.Fatalf("unexpected pattern: %v", mainRule[0])
}
mainOps, ok := mainRule[1].([]string)
if !ok || len(mainOps) != 1 || mainOps[0] != OpNoPush {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Same as the previous comment — Op is an alias (type Op = string), so mainOps[0] (type string) compared to OpNoPush (also string via the alias) is a same-type comparison. Tests pass.

Comment on lines +15 to +18
import { encodeRefsClaim } from '../src/jwt_claims.ts';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch — fixed in d19009d. Now imports encodeRefsClaim from the dist barrel that loadGitStorage already loads, so the script runs under plain node.

unknwon added 2 commits May 22, 2026 17:18
Per-op REST options never had an `ops` field on main; my earlier commit
added one alongside `refs`. Remove it — `ops` belongs on the URL minting
path (RemoteURLOptions only), and per-op REST calls take `refs` only.
The live test's `import ... from '../src/jwt_claims.ts'` failed under
plain node (no TS loader). Take encodeRefsClaim from the dist barrel
that loadGitStorage already imports, so the script runs with `node`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants