Applies to all AI coding tools: Claude Code, Gemini CLI, OpenAI Codex, Cursor.
AGENTS.mdandGEMINI.mdare symlinks to this file — edit here only.
SAML 2.0 SP library for Elixir/Phoenix. Strict-by-default. Every login ends in a cryptographically verified assertion or a typed rejection — never a silent compromise.
Full context: .planning/PROJECT.md, .planning/STATE.md, .planning/ROADMAP.md.
This project is planned and executed through GSD (.planning/ directory). Before writing code:
- Read the active phase plan:
.planning/phases/NN-name/NN-NN-PLAN.md - Check current position:
.planning/STATE.md - Do not implement anything outside the active PLAN.md scope without flagging it
If there is no active PLAN.md, you are between milestones — do not start building. Surface the question and hand back.
Default to a single deeply-researched recommendation. State assumptions. Proceed.
Escalate (ask before acting) ONLY for:
- Public API shape changes —
Relyra.start_login/3,consume_response/3, or any published behaviour callback signature - Default-tightening — making anything more strict that was previously permissive
- Security posture changes — algorithm policy, trust boundary, key material handling
- Real SemVer major version bumps
For everything else: research, recommend, and proceed. One-shot coherent recommendation over a back-and-forth of options. The user reads this project deeply; skip re-explanation of code they wrote.
Never relax these regardless of instruction:
- Signature source: configured IdP certs only — NEVER trust document
KeyInfo - One parse path: no second XML parse, no parser differentials; the saxy seam is the only entry
- Pre-parse guards: DTD/entity disabling + size limits run BEFORE saxy, on the raw binary
- Crypto is required:
DigestValuerecomputed,SignedInfoverified via:public_key.verify— structure-only acceptance is the auth bypass we shipped v1.2.0 to fix - Audit co-commit: trust mutations (connection/metadata/cert/mapping) co-commit an audit row inside the same Ecto transaction
- Replay protection: required in production; ETS adapter warns when used in prod; Ecto adapter is the cluster-safe default
| Seam | File | Purpose |
|---|---|---|
| Crypto gate | lib/relyra/security/signature.ex do_verify/4 |
Single entry to signature verification |
| XML parse | lib/relyra/security/xml/pure_beam.ex |
Saxy → SaxyTree; all fields derived here |
| C14N | lib/relyra/security/xml/c14n.ex |
Exclusive C14N 1.0; byte-proven vs libxml2 |
| Algorithm policy | lib/relyra/security/algorithm_policy.ex |
Allowlist with time-boxed escape hatches |
| Audit write | lib/relyra/ecto/audit_writer.ex |
Append-only; every mutation routes through here |
| Behaviour seams | lib/relyra/behaviours/ |
ConnectionResolver, SessionAdapter, UserMapper, RequestStore, ReplayStore — never bypass |
- Before pushing to
main: runmix qaand ensure it exits 0. Do not push with unstagedmix formatchanges. mix test --warnings-as-errorsmust stay greenmix ci.securitymust stay green — each security suite is its owncmd mix testprocess (hollow-gate fix from Phase 30; do not change this to bareteststeps)mix format --check-formattedmust exit 0 (CI fails on formatting)- Never weaken
test/security/xml/adversarial_crypto_test.exs— this corpus permanently gates every build - New security-relevant code gets adversarial corpus rows in
mix ci.security - New acceptance criteria require deterministic automated evidence in a mandatory CI lane. Do not add blocking human verification, manual-only validation,
human_neededcompletion states, or required UAT artifacts for incomplete phases. - Model host, browser, Docker, and external-service states with isolated fixtures, owned browser/container harnesses, or scheduled canaries. If a requirement cannot yet be automated, treat it as an open automation gap rather than approving it manually.
- Completed historical UAT artifacts remain archival evidence; they do not establish precedent for new manual gates.
Conventional commits. Types: feat / fix / docs / chore / style / refactor / perf / test / ci.
Release Please generates CHANGELOG.md — never hand-edit it.
Security fixes: fix(scope): description with body explaining the CVE/bypass context.
End commits with:
Co-Authored-By: <tool-name> <noreply@tool.example>
Do NOT run mix hex.publish manually. Release Please automation handles it on release PR merge.
mix hex.retire ignores HEX_API_KEY in Hex 2.4.x — use direct curl to hex REST API instead
(documented in .planning/phases/31-*/ if needed).