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
4 changes: 2 additions & 2 deletions .github/workflows/authcontract-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout PR test-merge composition
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# Default ref for a pull_request-triggered workflow is GitHub's
# own ephemeral merge commit (refs/pull/<n>/merge, i.e.
Expand All @@ -38,7 +38,7 @@ jobs:
- name: Fetch current base
run: git fetch origin "${{ github.event.pull_request.base.ref }}"

- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"

Expand Down
19 changes: 8 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,16 @@ jobs:
matrix:
python-version: ["3.10", "3.12"]
steps:
# Actions are pinned to immutable commit SHAs, not to mutable major tags:
# a tag can be repointed by its publisher, so `@v4` does not identify the
# code that will actually run. Each SHA below is the exact commit its
# named tag resolved to when it was pinned.
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ matrix.python-version }}
# -c constraints.txt fixes the resolved dependency versions, so CI
# measures the same dependency set the benchmarks were bound to.
- run: pip install -e ".[test]" -c constraints.txt
# The no-network reinstall later in `make ci` must find the declared build backend
# locally. Python 3.12+ runner virtual environments do not guarantee setuptools.
- run: python -m pip install "setuptools>=61,<85"
- run: python -m pip install -e ".[test]" -c constraints.txt
- name: Record the exact installed dependency set
run: pip freeze --exclude-editable
- run: pytest -q
- name: Public falsification harness
- run: make ci
- name: AC-039 public falsification harness
run: python3 falsify.py
62 changes: 20 additions & 42 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
@@ -1,64 +1,45 @@
name: Security

# Automated dependency-advisory monitoring (AC-039).
#
# Audits the CONTROLLED dependency set in constraints.txt — the exact versions
# CI installs and the benchmarks are bound to — against the PyPI/OSV advisory
# databases via pip-audit, the PyPA-maintained tool for this ecosystem.
#
# The schedule matters: an advisory can be published against an unchanged
# dependency set, so a check that only ran on push would go stale silently.
#
# SCOPE, stated exactly: this audits THIRD-PARTY DEPENDENCIES only. It does not
# analyse AuthContract's own source for security defects, and a green run is
# not an audit. See SECURITY.md §5.

# Automated dependency-advisory monitoring (AC-039). This workflow audits
# third-party dependencies only; a green run is not a security audit.
on:
push:
pull_request:
schedule:
# Weekly, so a newly published advisory against an unchanged dependency set
# surfaces without waiting for the next commit.
- cron: "17 6 * * 1"

permissions:
contents: read

jobs:
dependency-advisories:
name: Dependency advisory audit
dependency-review:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0

advisory-scan:
name: Dependency advisory audit and SBOM
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"

- name: Install the auditor at a pinned version
run: pip install "pip-audit==2.10.1"

- name: Audit the controlled dependency set
run: |
# --no-deps audits exactly the pinned closure in constraints.txt
# rather than re-resolving it, so the audited set is the installed
# set. --strict fails if any listed dependency could not be audited,
# so "no findings" cannot mean "nothing was checked".
#
# There is deliberately no severity threshold and no ignore list:
# ANY known advisory against a pinned dependency fails this job. That
# is stricter than the HIGH/CRITICAL floor it has to satisfy. A
# finding is never suppressed to make this workflow green — it is
# fixed by upgrading, or adjudicated as an explicit exception by a
# human, in the open.
set +e
pip-audit -r constraints.txt --no-deps --strict \
--format json --output pip-audit.json
rc=$?
set -e
echo "--- pip-audit report (inspectable evidence) ---"
cat pip-audit.json
echo
echo "--- summary ---"
python3 - <<'PY'
import json, sys

Expand All @@ -75,20 +56,13 @@ jobs:
report = json.load(open("pip-audit.json"))
deps = report.get("dependencies", [])
audited = {norm(d["name"]) for d in deps}

findings = [d for d in deps if d.get("vulns")]
print(f"pinned: {len(pins)} audited: {len(audited)}")
for d in findings:
for v in d["vulns"]:
fixed = ", ".join(v.get("fix_versions") or []) or "no fix listed"
print(f"VULNERABLE {d['name']}=={d['version']} {v['id']} (fix: {fixed})")
print(f"{len(findings)} dependency/dependencies with known advisories")

# Coverage assertion. The auditor drops a pin it cannot resolve in the
# advisory service and still exits 0, so "no known vulnerabilities"
# can otherwise mean "this package was never checked". Treat an
# unaudited pin as a failure, not as a pass. There is deliberately no
# allowlist here: the fix is to pin a version with advisory coverage.
unaudited = sorted(set(pins) - audited)
if unaudited:
print("UNAUDITED PINS (advisory coverage missing):")
Expand All @@ -98,3 +72,7 @@ jobs:
print("coverage OK: every pinned dependency was audited")
PY
exit $rc
- name: Install project for candidate SBOM
run: pip install -e ".[test]" -c constraints.txt
- name: Generate candidate SBOM
run: make sbom
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ __pycache__/
*.pyc
*.egg-info/
.pytest_cache/
.venv/
build/
14 changes: 14 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,20 @@ disposition, exit `0`. Run this rather than assuming the refusal paths work.

**Not on PyPI.** `pip install authcontract` will not work. Install from source only.

## 4.1 Real repository skills

Agents may use only these implemented workflows:

- `make test` — execute the committed test suite;
- `make falsify` — exercise the bounded PASS/refusal/tamper cases;
- `make no-network` — run the local install/import/test/CLI network guard;
- `make sbom` — generate the bounded candidate SBOM; and
- the six CLI commands listed below.

None of these commands publishes, deploys, attests a release, or adjudicates its
own result. Producers must state `NOT SELF-ADJUDICATED` and stop for independent
verification.

## 5. Supported CLI commands

Exactly six. Any other subcommand does not exist.
Expand Down
70 changes: 27 additions & 43 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,39 @@

## Current status: no unsolicited contribution workflow is established

This is stated plainly rather than left ambiguous, because an unstated process
wastes a contributor's time.

There is **no** established process for unsolicited pull requests. Specifically,
none of the following exists: a contributor licence agreement, a review-time
commitment, a maintainer rota, a triage service level, a governance model, a
code of conduct process, or a merge policy for outside contributions.

Nothing here promises any of those will exist. This is an experimental
reference implementation maintained as research and engineering evidence, not a
community-maintained project.

**Contribution is also constrained by licensing.** No license is declared (see
[`README.md`](README.md) § License), so the terms under which a contribution
could be accepted and redistributed are themselves unsettled. That is an owner
decision, not a process gap someone can work around.
There is no established process for unsolicited pull requests, contributor
licence agreement, review-time commitment, maintainer rota, governance model,
or merge policy for outside contributions. AuthContract is experimental
research and engineering evidence, not a community-maintained project. No
license is declared, so contribution acceptance and redistribution terms are
also unsettled owner decisions.

## What is genuinely useful right now

**Open an issue.** Issues are read, and they are the reliable path.

The most valuable thing you can send is a **falsification**: a case where
AuthContract's documented behaviour and its actual behaviour disagree.
Open an issue first. Include the exact commit SHA, OS, Python version, and a
minimal reproducer against the committed synthetic fixtures. Falsifications of
documented behaviour are especially useful:

- Run `python3 falsify.py` — the public falsification harness — and include its
output if a case failed.
- Include your OS, Python version, and the exact commit SHA.
- Reproduce against the committed fixtures in `fixtures/` where possible. No
credentials or network are needed.
- If a claim in `README.md`, `AGENTS.md`, or any document under `docs/` does not
hold, say which sentence and what you observed instead. A documented claim
that turns out to be false is a defect, and it is recorded rather than quietly
edited away.
- `make ci` runs the full producer verification surface;
- `make falsify` exercises the bounded Wave 1 harness; and
- `python3 falsify.py` exercises the AC-039 public harness.

For a **suspected security vulnerability**, do not open a public issue — follow
[`SECURITY.md`](SECURITY.md) instead.
For suspected vulnerabilities, do not open a public issue; follow
[`SECURITY.md`](SECURITY.md).

## Before writing code
If a code change is agreed, create a focused pull request rather than pushing
to `main`, include a regression test, preserve negative tests, report literal
command output at the exact commit, identify producer and proposed independent
verifier, classify claims as proved, measured, argued, or assumed, and state
`NOT SELF-ADJUDICATED`.

**Open an issue first.** A pull request that arrives without prior discussion
may sit unreviewed, and given the licensing situation above it may not be
mergeable at all. That is a genuine risk of wasted effort, so it is said up
front rather than discovered afterwards.
AI-assisted contributions should add these trailers when applicable:

If a change is agreed, the practical expectations are the same ones this
repository applies to itself:
```text
Agent-Assisted-By: <system and model>
Veraxis-Skill: <skill or workflow name>
Agent-Execution-ID: <optional attributable execution identifier>
```

- The full suite passes: `pytest -q` → 342 passed.
- The falsification harness passes: `python3 falsify.py`.
- A fix comes with a regression test that **fails without the fix**.
- Tests are not weakened, skipped, or deleted to make CI green.
- No claim is added that the repository's own measurements do not support.
Trailers are supplemental provenance. They do not establish authorship,
authority, independent verification, acceptance, or a licence grant.
24 changes: 24 additions & 0 deletions DEPENDENCIES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Dependency policy

AuthContract has one runtime dependency: `rfc8785`, which supplies the JSON
Canonicalization Scheme used in identity-bearing digests. Because a
canonicalization change can change artifact identity, the supported range is
bounded to the audited `0.1` line: `>=0.1.2,<0.2`.

The test extra bounds pytest to `>=7,<10`. Build tooling is declared separately
in `pyproject.toml`; it is not a runtime dependency.

This repository uses ranges for compatibility testing rather than claiming a
single universal lock across Python 3.10 and 3.12. Evidence must record the
resolved environment for the exact run. Pull requests receive dependency-diff
review and an advisory scan through `.github/workflows/security.yml`.
The scan upgrades its own `pip` environment before auditing and skips the local
editable AuthContract package, which is not a published PyPI dependency.

`make sbom` records the installed AuthContract and runtime dependency versions
in a deterministic CycloneDX document for the current candidate. It does not
attest a release, include operating-system packages, or establish that a
dependency is vulnerability-free.

Dependency updates that could affect canonical bytes, digests, exit behavior,
or receipt verification require the positive and negative verification suite.
18 changes: 18 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.PHONY: test falsify no-network sbom ci

PYTHON ?= python3

test:
$(PYTHON) -m pytest -q

falsify:
$(PYTHON) scripts/falsify.py

no-network:
$(PYTHON) scripts/verify_no_network.py

sbom:
$(PYTHON) scripts/generate_sbom.py --output build/authcontract.cdx.json

ci: test falsify no-network sbom

9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ pip install -e ".[test]"
Confirm the install:

```bash
pytest -q # expect: 342 passed
make test # expect: 342 passed
make falsify # expect: 4/4 bounded outcomes observed
```

> Not on PyPI. Install from source, as above.
Expand Down Expand Up @@ -300,6 +301,9 @@ Measured evidence and its limits: [`docs/BENCHMARKS-AC-039.md`](docs/BENCHMARKS-
| Report a suspected vulnerability | [`SECURITY.md`](SECURITY.md) |
| Understand the contribution situation | [`CONTRIBUTING.md`](CONTRIBUTING.md) |
| Understand the terminology | [`docs/DEVELOPER-LANGUAGE.md`](docs/DEVELOPER-LANGUAGE.md) |
| Review dependency and SBOM policy | [`DEPENDENCIES.md`](DEPENDENCIES.md) |
| Review security reporting | [`SECURITY.md`](SECURITY.md) |
| Review compatibility policy | [`VERSIONING.md`](VERSIONING.md) |
| See how this compares to other systems | [`docs/SOTA.md`](docs/SOTA.md) |
| Understand the full conceptual model | keep reading below |
| Use AuthContract from an AI coding agent | [`AGENTS.md`](AGENTS.md) |
Expand All @@ -318,7 +322,8 @@ Measured evidence and its limits: [`docs/BENCHMARKS-AC-039.md`](docs/BENCHMARKS-
contributor licence or review policy exists. Issues are the reliable path today.
If you are considering a substantive contribution, open an issue first so it
isn't wasted effort. [`CONTRIBUTING.md`](CONTRIBUTING.md) states exactly what
does and does not exist.
does and does not exist, including evidence, role-separation, and
agent-provenance expectations for an owner-agreed change.

**Security.** Do not report a suspected vulnerability through a public issue.
[`SECURITY.md`](SECURITY.md) sets out the triage policy and the current state of
Expand Down
46 changes: 46 additions & 0 deletions VERSIONING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Versioning and compatibility

AuthContract is currently version `0.0.1`: experimental and pre-1.0. The
repository has no published package or release compatibility promise. Pin an
exact commit SHA when reproducing or integrating it.

## Versioned surfaces

The following are public integration surfaces at a pinned commit, but may
change incompatibly before 1.0:

- the six CLI subcommands and their flags;
- exit codes (`0` for PASS/ALLOW, `1` for refusal or error);
- JSON `status`, `decision`, and `reason_code` values;
- the Python functions and result objects documented in `AGENTS.md`;
- contract artifact, runtime-fact, action, and receipt JSON structures; and
- canonicalization and digest rules used to bind those structures.

Human-readable messages are never a compatibility interface. Consumers should
branch on structured fields, while still pinning the exact commit because the
reason-code set is not yet frozen.

## Change rules

Before 1.0, a change to a CLI name or flag, exit semantics, reason code, Python
signature, required JSON field, canonicalization rule, or digest scope is a
breaking change. Such a change must:

1. be explicit in the pull request and documentation;
2. update positive and negative fixtures and tests;
3. identify affected receipts and artifacts;
4. avoid silently reinterpreting an existing digest; and
5. use a new artifact/schema/version identity when old and new bytes could
otherwise be confused.

Adding an optional field is compatible only when older consumers safely ignore
it and its presence cannot widen authority. A new refusal condition is treated
as behaviorally consequential even when it fails closed.

## Releases and artifacts

No distributable release or attestation is established. Source installs are the
only supported installation route. An SBOM may be generated for a candidate
commit with `make sbom`; it is evidence about resolved package metadata, not a
release attestation or security guarantee.

Loading