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
31 changes: 31 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Automated dependency-update monitoring (AC-039).
#
# Dependabot opens pull requests when a newer version of a pinned dependency or
# a pinned action is published, so constraints.txt and the action SHAs cannot
# quietly rot. Each PR runs the full CI matrix, the falsification harness, and
# the dependency-advisory audit before it can be considered.
#
# NOTE ON PROVIDER STATE: version-update PRs come from this file alone.
# Dependabot SECURITY alerts (and security-update PRs) are a separate repository
# setting that cannot be enabled from a file in the repository — the owner must
# turn on Dependabot alerts under Settings -> Advanced Security. That is why
# .github/workflows/security.yml exists: the advisory gate it enforces is
# repository-controlled and does not depend on that provider setting.

version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
commit-message:
prefix: "deps"

- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
commit-message:
prefix: "ci"
12 changes: 9 additions & 3 deletions .github/workflows/authcontract-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,19 @@ name: AuthContract Gate
on:
pull_request:

# Least privilege (AC-039). The gate reads the repository and adjudicates; it
# writes nothing back to GitHub. The default token grant is deliberately not
# relied on.
permissions:
contents: read

jobs:
gate:
name: AuthContract Gate
runs-on: ubuntu-latest
steps:
- name: Checkout PR test-merge composition
uses: actions/checkout@v4
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
# Default ref for a pull_request-triggered workflow is GitHub's
# own ephemeral merge commit (refs/pull/<n>/merge, i.e.
Expand All @@ -32,12 +38,12 @@ jobs:
- name: Fetch current base
run: git fetch origin "${{ github.event.pull_request.base.ref }}"

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

- name: Install
run: pip install -e ".[test]"
run: pip install -e ".[test]" -c constraints.txt

- name: Run AuthContract test suite against the merge composition
id: tests
Expand Down
23 changes: 20 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,33 @@ on:
push:
pull_request:

# Least privilege (AC-039). This workflow only reads the repository and runs
# tests: it publishes nothing, comments nothing, and touches no GitHub state.
# The default token grant is not relied on — prior runs succeeding under the
# default does not establish that the default is appropriate.
permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.12"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
# 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
with:
python-version: ${{ matrix.python-version }}
- run: pip install -e ".[test]"
# -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
- name: Record the exact installed dependency set
run: pip freeze --exclude-editable
- run: pytest -q
- name: Public falsification harness
run: python3 falsify.py
100 changes: 100 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
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.

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
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.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

def norm(name):
return name.lower().replace("_", "-")

pins = {}
for line in open("constraints.txt"):
line = line.split("#", 1)[0].strip()
if line:
name, version = line.split("==")
pins[norm(name)] = version

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):")
for name in unaudited:
print(f" {name}=={pins[name]}")
sys.exit(1)
print("coverage OK: every pinned dependency was audited")
PY
exit $rc
43 changes: 40 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,15 @@ python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[test]"
```

For the exact dependency versions CI and the benchmarks use, add the controlled
set: `pip install -e ".[test]" -c constraints.txt`. See
[`docs/VERSIONING.md`](docs/VERSIONING.md).

Verify: `pytest -q` → `342 passed`.

Falsify: `python3 falsify.py` → 5 cases, all matching their declared expected
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.

## 5. Supported CLI commands
Expand Down Expand Up @@ -241,11 +248,38 @@ Do not soften, omit, or paraphrase this ceiling when summarizing the project.
## 15. Licensing

**No license is declared.** No `LICENSE` file exists and `pyproject.toml`
declares no license field, so default copyright applies and no usage rights are
granted. Treat this as source-available for evaluation and reading. **Do not
declares no SPDX license field, so default copyright applies and no usage rights
are granted. `pyproject.toml` carries the Trove classifier
`License :: Other/Proprietary License` — a machine-readable statement that this
is **not** open source. That classifier is a description, not a grant. Treat this as source-available for evaluation and reading. **Do not
describe it as open source**, and do not assume redistribution or derivative
rights. Direct licensing questions to the repository owner.

## 15a. Version and interface stability

Package version is `0.0.1` and has never been published or incremented per
change. **The commit SHA is the only reliable identity** — two checkouts both
reporting `0.0.1` may differ. Semantic Versioning is **not** implemented or
claimed. Pre-1.0 interfaces may change without a major bump. The one commitment
made is that reason codes will not change meaning *silently* under the same
version. Full policy and the declared public surface:
[`docs/VERSIONING.md`](docs/VERSIONING.md).

## 15b. Falsification harness

`python3 falsify.py` runs five public cases — valid ALLOW, undeclared action,
stale fact, untampered receipt, tampered receipt binding — each against a
declared expected disposition, and exits non-zero on any mismatch in either
direction. Use it to verify refusal behaviour rather than asserting it.

## 15c. Security reporting

Do not open a public issue for a suspected vulnerability. See
[`SECURITY.md`](SECURITY.md). A private reporting route is **not yet
established** — that is recorded there as requiring owner action, not glossed
over. Automated dependency-advisory checking runs in CI; a passing scan is
**not** an audit and establishes nothing about this project's own code.

## 16. Prohibition on invention

If asked to do something this repository does not support:
Expand All @@ -258,6 +292,9 @@ If asked to do something this repository does not support:
5. Do **not** weaken or bypass a refusal to produce a desired outcome.

Further reading: [`README.md`](README.md) · measured evidence
[`docs/BENCHMARKS.md`](docs/BENCHMARKS.md) · maturity
[`docs/BENCHMARKS-AC-039.md`](docs/BENCHMARKS-AC-039.md) (current; `docs/BENCHMARKS.md` is
the superseded earlier baseline) · versioning
[`docs/VERSIONING.md`](docs/VERSIONING.md) · security
[`SECURITY.md`](SECURITY.md) · maturity
[`docs/TRL-ASSESSMENT.md`](docs/TRL-ASSESSMENT.md) · planned work
[`docs/ROADMAP.md`](docs/ROADMAP.md).
56 changes: 56 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Contributing

## 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.

## 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.

- 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.

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

## Before writing code

**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.

If a change is agreed, the practical expectations are the same ones this
repository applies to itself:

- 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.
Loading
Loading