Skip to content

Org signing policies: declarative rules for who can sign what #168

@bordumb

Description

@bordumb

Problem

Sigstore's OIDC model makes organization signing policies trivial: "only members of @google.com can sign releases for this project" is a one-line check against the OIDC email claim. It works because identity is bound to a corporate directory.

Auths doesn't have OIDC — by design. But we currently make org-level signing policies harder than they need to be. A project maintainer who wants to enforce "only these 3 people can sign releases" has to manually manage attestation chains and capability delegations. That's the right cryptographic foundation, but the UX is missing.

To compete with and beat OIDC-based policies, we need a declarative policy surface that's simpler than OIDC while being stronger cryptographically.

What "beating OIDC" looks like

OIDC policies have real weaknesses we can exploit:

OIDC weakness Auths advantage
Policy depends on IdP availability Policy is local, verifiable offline
IdP admin can silently add signers Adding a signer requires a signed attestation (auditable)
Email-based identity is spoofable (provider compromise) DID-based identity is cryptographically bound
No capability scoping — you can sign anything Capabilities are per-attestation (sign_commit, sign_release, manage_members)
No time-bounding — valid until account disabled Attestations have expires_at — automatic expiry, no revocation needed
Policy changes are invisible Policy changes are KEL events — append-only, auditable

Proposed: .auths/policy.toml

A declarative policy file in the repo that defines signing rules:

[policy]
# Who can sign releases
[[policy.release]]
signers = [
  "did:keri:ELeNxCv0...",   # alice — lead maintainer
  "did:keri:EKq9x8Pf...",   # bob — release manager
]
require = 2                   # 2-of-2 signatures required
capabilities = ["sign_release"]

# Who can sign commits (broader group)
[[policy.commit]]
signers = [
  "did:keri:ELeNxCv0...",
  "did:keri:EKq9x8Pf...",
  "did:keri:EHr7mPQj...",   # carol — contributor
]
require = 1
capabilities = ["sign_commit"]

# CI ephemeral signing — constrained by workflow identity
[[policy.ci]]
allow_ephemeral = true
require_log = "sigstore-rekor"       # must be logged
max_ttl = "1h"                        # attestation expires in 1 hour

Key design principles

  1. Policy is data, not code. A TOML file that anyone can read. No Rego, no OPA, no policy engine. The auths-policy crate already exists — extend it.

  2. Policy is signed. The .auths/policy.toml file is committed to the repo and covered by the commit signature. Changing the policy requires a signed commit from someone authorized to do so. This is strictly stronger than OIDC policies, which are invisible mutations in a corporate directory.

  3. Policy is verifiable offline. No network calls to check "is this signer allowed?" The verifier reads the policy file and checks the attestation chain. Works on a plane, works air-gapped.

  4. Threshold signatures built-in. "2-of-3 maintainers must sign a release" is a first-class concept, not bolted on. OIDC has no equivalent — it's always 1-of-N (whoever has a valid token).

  5. Capability scoping. A signer can be authorized for sign_commit but not sign_release. OIDC tokens are all-or-nothing — if you can authenticate, you can sign anything.

  6. Time-bounded by default. CI attestations expire. Contributor access expires. No "forgot to revoke" problem. OIDC short-lived certs address this for signing keys but not for authorization policies.

UX target

# Maintainer sets policy
auths policy init                        # creates .auths/policy.toml scaffold
auths policy add-signer <DID> --role release
auths policy set-threshold release 2

# Contributor signs (works if they're in the policy)
auths artifact sign release.tar.gz       # checks policy, signs if authorized

# Verifier checks (works offline)
auths artifact verify release.tar.gz     # checks signature + policy compliance

What exists today

  • auths-policy crate with policy expression engine
  • Capability system (sign_commit, sign_release, manage_members) in attestations
  • Org management (auths org create, auths org add-member) with role-based attestations
  • Threshold policy types in auths-verifier

The pieces exist. What's missing is the declarative policy file, the CLI surface, and the verification integration.

Non-goals for v1

  • Federation / cross-org policies (future)
  • Dynamic policy changes without commits (contradicts "policy is signed")
  • Compatibility with OIDC claims (we're replacing them, not bridging to them)

Success criteria

A project maintainer can:

  1. Define who can sign what in a single file
  2. Enforce threshold signatures on releases
  3. Time-bound CI signing credentials
  4. Verify policy compliance offline with no external dependencies
  5. Audit every policy change in git log

All of this without any account on any platform, any OIDC provider, or any network access.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions