Repository navigation
Verify bound acceptance evidence; accepted copy for the verified customer - #11
Merged
Merged
Conversation
…cepted copy A bound acceptance retains the exact terms bytes the signer was shown and the exact Target Summary, each beside the digest the signer's form carried. Job creation and every later liveness check recompute them: any missing, oversized, mismatched, stale, repeated or partial piece refuses the job, and the job row pins the summary digest so later edits end the authorization. Config `bound_terms_versions` lists versions that must be bound; the plugin never reads mutable page HTML. With `send_accepted_copy` (off by default) a successful mailbox confirmation sends the verified contact the Target Summary, hashes, filled-in copy and the exact terms bytes. At most once per job (compare-and-set), only to the verified address, uncertain outcomes never resent, and independent of the scan. Schema 4: summary_sha256, accepted_copy_status, accepted_copy_at. Refs CompleteTech-LLC/complete.tech#17 (gap 2, plugin side). Co-Authored-By: Claude Code <noreply@anthropic.com>
…Target Summary; harden the accepted copy Review findings: enabling bound_terms_versions cancelled in-flight unbound jobs (requirement and profile are now frozen in the job row); the summary check let decoy or appended lines ride along into the customer email (the summary must now equal the canonical four lines exactly); the accepted copy now applies the report path's recipient rule, writes the attachment into a private random directory and attaches only bytes that reproduce the recorded hash. Co-Authored-By: Claude Code <noreply@anthropic.com>
…mmary carries limits and period Merge of the admission-guard fixes plus: - Every terms version requires bound evidence unless the operator lists it as legacy in terms_policy, and legacy is honoured only for r2-or-earlier versions; the loader refuses to enable unless supported_terms_version has an explicit entry, so bumping the version cannot silently drop the requirement. - ct_scan_target_summary() is the one canonical Target Summary and now includes the profile's limits and the authorization period. Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
This was referenced Oct 22, 2026
- ct_scan_check_bound_evidence keyed its checks by the computed hash, so a terms snapshot byte-identical to the Target Summary overwrote the terms entry and the recorded terms hash / shown digest were never compared. Use a list of [computed, recorded] pairs so every comparison runs. - Refuse (ct_scan_evidence_duplicate) when an evidence key holds more than one meta row instead of silently using the first. - Accepted copy: compare the recorded terms hash case-insensitively, and re-check under the queue lock that the job is still live (queued, leased or running, authorization live) before claiming the send. - Tests: identical-bytes cases, duplicate rows at creation and while running, upper-case terms hash, and no copy for cancelled, revoked or edited-evidence jobs. - docs/WORDPRESS.md: document the above, list bound_required in schema 4, fix the missing blank line before Egress. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
CompleteDotTech
changed the base branch from
feat/request-admission-guard
to
main
October 23, 2026 19:38
CompleteDotTech
marked this pull request as ready for review
October 23, 2026 19:40
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
CompleteDotTech
added a commit
that referenced
this pull request
Oct 23, 2026
Brings in the #10 and #11 review fixes (probe busy/inconclusive handling, bound-evidence pair comparisons, duplicate evidence refusal, accepted-copy live check). Resolutions: - admission.py probe(): keep main's bounded queue_busy retry and use this branch's per-kind route ("probe" for http, "control_probe" for control). Test added for the control route. - ct_scan_send_accepted_copy(): main's live-job check allowed only queued/leased/running, but confirmation now leaves control-required jobs in awaiting_control and sends the copy then, so the default path would never send it. awaiting_control is accepted as live; every other check (authorization live, recipient, terms hash, compare-and-set) is kept. evidence-strict.php covers confirmation into awaiting_control; docs updated. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
CompleteDotTech
added a commit
that referenced
this pull request
Oct 23, 2026
Brings in the merged #10/#11/#12 review fixes (probe busy-retry, list-of-pairs evidence comparison, control_admitted and 409 already_admitted, fail-closed ct_scan_control_required(), terms_policy docs) under the retention branch. Conflict: the schema's required jobs columns now list both control_admitted and the retention/hold/identity columns. The schema version moves to 7 so an install that recorded 6 before control_admitted existed re-runs the upgrade. Retention coherence with the control gate: awaiting_control (pending or checking) is not terminal and is never closed; a restored row recognised by a tombstone is no longer deleted while it holds a lease (leased/running scan or a control check in progress), so the one-at-a-time gate is never freed while its traffic may be in flight. Tests cover both. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs CompleteTech-LLC/complete.tech#17 (gap 2, plugin side). Stacked on #10 (base =
feat/request-admission-guard; retarget tomainafter #10 merges). Scanning stays disabled by default; both new behaviours are off unless configured.What changes
_ct_terms_snapshot) and the exact Target Summary (_ct_summary_text), each next to the digest the signer's form carried. Job creation and every later liveness check (ct_scan_authorization_is_live: claim, start, admit, result) recompute them: missing, oversized (512 KiB / 8 KiB), mismatched, stale-form, repeated-field or partial evidence refuses the job (ct_scan_binding_mismatch/ct_scan_binding_required). The job row pinssummary_sha256, so editing retained evidence after the fact cancels a queued/running job. Configbound_terms_versions(default empty) lists versions that MUST be bound; unmarked-but-partial evidence is always refused. The plugin never fetches mutable page HTML.send_accepted_copy, default off): after a successful mailbox confirmation, one email to the verified address with the Target Summary, hashes, filled-in copy and the exact terms bytes as an attachment. At most once per job (compare-and-set claim before hand-off), never to a referrer, uncertain outcomes recordedunknownand never resent,accepted= transport acceptance not receipt, failure cannot cancel/delay/repeat a scan. Schema 4:summary_sha256,accepted_copy_status,accepted_copy_at.Fail-closed behavior
Any evidence defect before or after job creation refuses/cancels; no fallback to "legacy" when evidence is present but wrong; unreadable/oversized meta refuses; the accepted-copy path returns an error without a confirmed recipient, and mail faults/throws are swallowed into
unknownwithout touching the job.Tests
integrations/wordpress/tests/evidence.php(+evidence-strict.phpin a second process with binding required and copy on): 16 single-defect refusals, byte round trip with multibyte/quotes, post-creation edit cancels, copy sent to verified contact only with exact attachment bytes, no temp file left, idempotent, mail false/throw =>unknown, no resend, unconfirmed => error, disabled => nothing sent. Run against disposable MariaDB/WordPress; mutation-checked (9 mutants caught). CI green on the head. Cross-repo end to end (recorder from the companion complete.tech PR -> this plugin) verified locally.Reviewer: please attack
Digest comparison and normalisation (case, whitespace, encoding), anything that lets a request become a job with evidence the signer did not see, TOCTOU between evidence checks and job insert (checks run under the queue lock), temp-file handling for the attachment, recipient selection.
Needs the owner
TODO(owner)in code); customer-facing copy was not approved.ct_terms_digest/ct_summary_digest; that is the step-5 release and owns form/CTA copy.post_content; WordPress kses entity-encodes&/<for anonymous submitters, so a company name such as "AT&T" saves as a lead that is not queued. Worth a decision before activation.Second independent review: fixes landed
bound_terms_versionscancelled in-flight unbound jobs -> the requirement and the profile are now frozen per job (bound_required).is_email, saved address, terms version/hash); attachment written to a private random directory and attached only if it reproduces the recorded hash; short writes handled.evidence.phpspawnsevidence-strict.phpitself (binding required + copy on), so the strict path is covered.Accepted: the confirmation page still waits for the lock and the SMTP hand-off before responding.
Fail-closed review round 2 (coordinator findings)
Fixed in this stack, each with tests that fail without the fix (mutation-checked):
egress_enforced: true(loader refuses otherwise); docs/README/example config state it; unverified nftables egress sketch added (not a claim of enforcement). In-flight bound is exactly 10 s from the grant (no grace). Authorization read from the DB, not the object cache. A completed result needs >= 1 admitted request. Failed schema upgrade: oneerror_log, 5-minute back-off, plugin 0.2.0. Special-purpose IP ranges listed explicitly (CGNAT, documentation, benchmarking,3fff::/20, 6to4, NAT64, ...), tested independent of the interpreter'sipaddress.terms_policy, strict by default;legacyonly for r2-or-earlier); the loader refuses to enable without an explicit entry for the served version.ct_scan_target_summary()is the one canonical summary and now includes limits and period.closed_at >= created_atand a sane clock; retention runs outside the queue lock (own lock, throttled, one short queue lock per deletion) so Stop never queues behind a batch.