Maintainer Conformance Workflow
Copy MarkdownThis is supplemental maintainer evidence. It is not OpenID certification, it is
not a release gate or milestone-closing proof, and it is not a broader
product-support claim. The
canonical Lockspire support contract remains docs/supported-surface.md; the
repo-native tests remain the primary executable proof.
What is pinned
The external inputs are locked in
scripts/conformance/oidf-suite-lock.json and validated before use:
- OpenID Foundation conformance suite
release-v5.1.43at commit16ad152b1b2c0baacd3d2519128340d95deb2b8c - the suite archive and four required helper files by SHA-256
- server, nginx, and MongoDB images by immutable
sha256:digest - the Phase 37 and FAPI 2.0 plan files by the checksum recorded in each receipt
Mutable tags such as latest, branch heads, checksum fallback, and unpinned
container images fail closed. Updating the suite is a reviewed lock update: pin
the new commit, downloads, checksums, and image digests together, then rerun the
input contracts and both profiles.
Local prerequisites and commands
Install Docker with the Compose plugin, Python 3 with the OIDF runner's
httpx and pyparsing modules, curl, and jq. Missing Python modules are caught
by the runner preflight and classified as infrastructure failure. Check the
local tooling and immutable lock without printing environment variables or
secrets:
mix lockspire.oidf_conformance --check
The check reports missing tools or inputs before downloads and points to the two exact profile commands:
bash scripts/conformance/run_phase37_suite.sh
bash scripts/conformance/run_fapi2_suite.sh
Those low-level commands require LOCKSPIRE_OIDF_PROVIDER_CONFIG to name a
private JSON configuration file in the format accepted by the pinned OIDF
runner. For the normal repository proof, use the throwaway wrapper instead:
LOCKSPIRE_OIDF_EPHEMERAL_DB=true \
bash scripts/conformance/run_ephemeral_oidf_profile.sh phase37
LOCKSPIRE_OIDF_EPHEMERAL_DB=true \
bash scripts/conformance/run_ephemeral_oidf_profile.sh fapi2
The explicit database acknowledgement prevents the wrapper from resetting or
seeding a normal developer database by accident. The wrapper boots the Billingo
adoption app on a disposable database, binds it only for the duration of the
run, generates client keys and provider JSON inside a mode-700 temporary
directory, and deletes the directory and private host log on exit. The pinned
suite nginx container terminates disposable TLS for the provider and forwards
only the demo's OAuth, login, and protected-resource paths through Docker's
explicit host.docker.internal:host-gateway mapping. The suite keeps ownership
of its /test routes. No generated key, certificate, or provider JSON is a
repository or Actions secret.
For a hosted provider, the private JSON file supplies the provider discovery
URL, client material, and any browser automation needed by that environment;
it is read in place, never copied into retained evidence, and must not be
committed. The checked-in profile JSON selects the suite plan, variants, and
modules. After Compose has reported readiness, Lockspire translates that
selection into the pinned scripts/run-test-plan.py CLI and makes its exit
status authoritative.
Lockspire first uses the runner's non-network --list mode to catch invalid
plan syntax, missing Python dependencies, or an incomplete verified archive as
an infrastructure_failure; a later nonzero suite result is suite_failure.
CI pins CPython 3.13.15, pip 26.2.1, and the actions/setup-python commit.
The runner's complete Python dependency closure is exact-version and
wheel-SHA-256 locked in scripts/conformance/runner-requirements.lock; pip runs
with dependency resolution disabled and verifies the installed versions. This
is deliberately stricter than the upstream suite's unversioned local install
instructions.
Raw downloaded helpers, normalized Compose configuration, the runner's full stdout/stderr, exported suite results, and suite work stay in a private temporary directory and are deleted after the run. The retained profile directories are limited to:
.artifacts/conformance/phase37/receipt.json
.artifacts/conformance/fapi2/receipt.jsonEach schema-versioned receipt contains only the profile, pinned suite tag/commit, input hashes and image digests, plan name/checksum, Python version, bounded result names, timestamps, and a status/classification. It cannot contain URLs, authorization material, cookies, tokens, secrets, passwords, raw logs, or provider configuration.
Scheduled and manual runs
.github/workflows/oidf-conformance.yml runs the repo-native Phase 37 proof and
both external profiles weekly from the default branch, and remains manually
dispatchable. The two scheduled jobs are self-contained: each gets a fresh
PostgreSQL service, runs the real embedded Billingo host, mints throwaway
provider/client material, and destroys that material after the pinned suite
returns. This preserves the embedded-library boundary because Billingo still
owns accounts, login, consent presentation, branding, and product policy; CI
merely provisions that host automatically.
The hosted maintainer lane is manual-only. Select run_hosted_lane during a
dispatch after configuring the separate optional
LOCKSPIRE_OIDF_HOSTED_PROVIDER_CONFIG_JSON secret. That is the only provider
configuration secret this workflow needs. GitHub exposes it only to the hosted
runner step; Lockspire writes it to a mode-600 file in the private temporary
run directory, removes it from the child-process environment, and deletes it at
exit. A missing or malformed hosted secret fails before suite startup with an
infrastructure_failure receipt. The schedule remains a supplemental
reliability history and does not block CI or publication.
Workflow permissions are read-only, overlapping runs are canceled, jobs have
timeouts, and only validated receipt.json files are retained for a bounded
period.
Reading failures
Classify a failed run before treating it as a protocol defect:
infrastructure_failure: immutable download, checksum, Docker, Compose, network, startup, or evidence-production failure. Repair the environment or reviewed input lock, then rerun the same commit.suite_failure: the external suite ran and reported a profile result that Lockspire did not satisfy. Reproduce against the same suite commit, plan, and image digests; correlate with repo-native tests before changing protocol code.integration_only: the explicit safe skip mode exercised integration and receipt plumbing but did not run the external suite. It is never a conformance pass.success: the pinned runner completed and emitted a validated receipt. This is still supplemental evidence, not certification.
Never upload a temporary work directory to diagnose a failure. Inspect raw output only inside the ephemeral job/local run, redact it before sharing, and retain the bounded receipt as the durable identity of the run.
Repo-native proof remains first
Before drawing conclusions from the supplemental lane, run the applicable
Lockspire integration and release-readiness tests. The FAPI boundary probe at
scripts/conformance/fapi2-check.sh remains a useful targeted HTTP smoke test
against a configured host, but it is not a substitute for either repo-native
behavior proof or formal external certification.