Implement Registration Spam Prevention #999

Closed
opened 2026-10-27 11:22:03 -05:00 by Codex · 2 comments
Member

Registration should prevent automated spam accounts while keeping account creation straightforward and accessible for legitimate members.

The current Fortify registration flow already limits attempts to three per IP address per hour and records suspicious registrations for review. Add preventative protection before account creation, complementing those controls. Evaluate CAPTCHA and alternatives such as a honeypot, submission timing checks, or risk-based challenges; select and document an approach based on effectiveness, accessibility, privacy, reliability, and user friction rather than requiring a specific vendor upfront.

Scope

  • Registration form and Fortify server-side account creation flow.
  • Selection and implementation of a suitable spam prevention mechanism, which may combine multiple controls.
  • Direct registration POST requests as well as browser submissions.
  • Configuration, operational visibility, and any external verification service integration.
  • Existing registration throttling, validation, suspicious-registration auditing, and successful account provisioning.
  • Existing spam account cleanup and protections on unrelated forms are outside this issue.

Acceptance Criteria

  • Document the selected approach and why it fits registration spam prevention, including accessibility, privacy, operating cost, and service availability considerations.
  • Enforce protection on the server before creating an account or related records, provisioning a Fider user, or triggering registration delivery side effects; direct POST requests cannot bypass it.
  • Reject submissions that fail the selected checks with a clear, accessible message and a practical retry path for legitimate users.
  • If a challenge/token service is selected, verify submissions server-side and handle missing, invalid, expired, and replayed tokens according to the provider's verification contract.
  • Define and test behavior for verification timeouts, provider failures, missing configuration, and blocked challenge scripts; failures must not silently disable protection.
  • Preserve existing username/email validation, password requirements, IP throttling, suspicious-registration auditing, and successful registration behavior.
  • Support the existing responsive layout, light/dark themes, keyboard navigation, and form error handling. Never repopulate passwords after an error.
  • Keep secrets in environment-backed configuration and out of source control, client markup, and logs; document setup and rollback. Any new dependency requires approval under repository policy.
  • Provide enough operational visibility to evaluate blocked attempts and false positives without logging passwords, secrets, or challenge tokens.

Test Coverage Required

  • Extend tests/Feature/Auth/RegistrationTest.php or add focused registration spam prevention coverage.
  • Cover legitimate registration and rejection of bot-like or failed-verification submissions, including direct POST attempts.
  • Assert rejected attempts create no user or associated account records and trigger no external provisioning or registration delivery side effects.
  • Cover missing/invalid/expired/replayed challenge tokens where applicable, plus provider errors, timeouts, and configuration failures using fakes without live network calls.
  • Preserve existing registration throttling and validation coverage and tests/Feature/SuspiciousRegistrationTest.php behavior.
  • Verify accessible errors, retry behavior, and registration form rendering for the selected mechanism.
  • Run focused registration and suspicious-registration tests with the testing environment before closing the issue.
  • Run vendor/bin/pint --dirty --format agent and composer stan if PHP changes; run npm run build if shared frontend assets change.

Progress Checklist

  • Evaluate CAPTCHA and alternative controls and document the selected approach
  • Define configuration, failure behavior, accessibility, and rollout/rollback requirements
  • Implement server-side prevention before account creation and registration side effects
  • Add any required accessible form controls and retry/error states
  • Preserve existing throttling, validation, auditing, and provisioning behavior
  • Add focused spam prevention and registration regression coverage
  • Verify desktop/mobile, keyboard access, and light/dark themes
  • Run focused registration and suspicious-registration tests
  • Run Pint, PHPStan, and the asset build where applicable
  • Document configuration, operational verification, and rollback
  • Move the issue out of Codex review once verification is complete

Issue structure follows #853.

Acceptance review — November 5, 2026

Implementation and local verification meet the acceptance criteria and required test coverage. The checklist above records implementation completion; it does not claim production deployment or a live Cloudflare verification test.

Requirement Implementation and evidence
Approach selection The tracked README compares managed Turnstile, hCaptcha, reCAPTCHA, honeypots, and timing checks, including friction, accessibility, privacy, cost, availability, and limitations. Managed Turnstile was selected without adding dependencies.
Server enforcement before side effects CreateNewUser invokes RegistrationSpamVerifier after the existing throttle and account-field validation but before the account transaction, related records, suspicious-registration audit, Fider provisioning, and Fortify registration events. Direct POST and JSON requests cannot bypass verification.
Token and provider contract Strict success, action register, and an environment-backed exact hostname allowlist are required. Missing/malformed tokens, invalid/expired/replayed tokens, context mismatches, provider errors, malformed responses, redirects, timeouts, and missing configuration fail closed. HTTP requests use the fixed Siteverify endpoint, three-second connection and five-second overall timeouts, with no automatic retry. Siteverify owns single-use and expiry enforcement.
Accessible form and recovery The actual Blade form is tested at 320px and 1280px in light/dark themes. Keyboard retry, live error/status messages, expiration, interaction failure, blocked scripts, a stalled script timeout, compact/flexible sizing, disabled JavaScript, and real Livewire navigation are covered. Removed-widget callbacks cannot restore stale tokens. A failed verification followed by a fresh-token registration succeeds.
Existing behavior preserved Registration validation, password requirements, three-attempts-per-IP-per-hour throttling, restored Cloudflare visitor IPs, account-related records, Fider behavior, and suspicious-registration auditing retain focused regression coverage.
No rejected-registration side effects Negative feature tests compare counts across users, all associated account tables, and activity logs, assert no registration event or notification/security-alert delivery, and prohibit Fider or other outbound provisioning requests.
Privacy, observability, setup, and rollback Environment-backed keys and hostname configuration, fixed sanitized outcome/reason logs, Sentry registration URL and request-metadata minimization, and exclusion of passwords/tokens from restored form input are covered. The README documents setup, privacy/network requirements, failure behavior, operational checks, and rollback.

Verification completed on the local codex/issue-999-registration-spam-prevention branch:

  • Focused registration, spam prevention, email verification, suspicious-registration, and visitor-IP tests: 129 passed, using --env=testing and provider fakes.
  • Full PHP suite: 1,549 passed, 2 skipped (1,551 total), using --env=testing -d memory_limit=1G. The default 128 MB CLI limit was insufficient for full-suite discovery. Existing duplicate-constant warnings in unrelated tests remain; the clean sequential full run has no failures.
  • Playwright registration, email verification, and shared-JavaScript checks: 20 passed, using local fixtures and a fake provider, with no live verification requests.
  • vendor/bin/pint --dirty --format agent, explicit Pint checks for new PHP files, composer stan, npm run build, and git diff --check: passed.

The administrative checklist condition is already satisfied: the issue is labeled status/in progress and has no Codex-review status to remove. It remains open and assigned to Codex. Changes remain local and uncommitted; no PR or deployment has been created. Production environment values and a live widget/registration check remain deployment-time verification steps.

Follow-up acceptance review — November 5, 2026

A second review found and corrected a diagnostic privacy gap: Sentry captures the full request URL and query string independently of the request body. The original body filter could therefore retain a password or challenge token submitted in a registration query string; referrer headers could also repeat those values. Registration request diagnostics now retain only the query-free URL and HTTP method, excluding bodies, query strings, headers, and cookies. Matching follows Laravel's decoded/trailing-slash-normalized path and honors Fortify's configured registration path and prefix. Unrelated request diagnostics retain their existing behavior.

The new regression initially failed against the old implementation. Eight added cases now pass across Sentry events/transactions and canonical, trailing-slash, encoded, and configured registration paths. The README documents this policy. Fresh verification: 127 focused tests passed, 1,547 full-suite tests passed with 2 skipped, Pint, PHPStan, and whitespace checks passed. The full suite used the temporary 1 GB test-process memory limit and ran sequentially. Frontend assets and behavior were unchanged in this follow-up; the previously recorded 20 browser checks and successful asset build remain applicable.

All 11 implementation checklist items remain complete. No further implementation gaps were found. The issue remains open and in progress; no PR or deployment was created, and live Cloudflare verification remains a deployment-time check.

Stack-trace privacy acceptance review — November 5, 2026

An additional review reproduced another diagnostic leak using the installed Sentry SDK's real stack-trace builder: registration input arrays were retained as frame argument values, including passwords and challenge tokens. Filtering request metadata alone does not remove those arguments. RegistrationDiagnostics now clears argument values from the event's primary stack trace and every chained exception stack trace for registration requests, preserving the frames and their file/line/function information. Unrelated-route stack traces keep their existing argument behavior. The README documents this policy.

The regression failed before the fix and passes for both event and transaction callbacks afterward. Fresh verification: 129 focused PHP tests passed; the sequential full suite passed 1,549 tests with 2 skipped (1,551 total) using the temporary 1 GB test-process memory limit. Pint, PHPStan, and whitespace checks passed. No frontend behavior or assets changed in this review, so the previously recorded 20 passing browser checks and successful asset build remain applicable.

All 11 implementation checklist items remain complete, with no other concrete gaps found against the issue's description, acceptance criteria, and required coverage. The issue remains open and in progress. No PR, deployment, dependency change, or production-data change was made; live Cloudflare verification remains a deployment-time check.

Registration should prevent automated spam accounts while keeping account creation straightforward and accessible for legitimate members. The current Fortify registration flow already limits attempts to three per IP address per hour and records suspicious registrations for review. Add preventative protection before account creation, complementing those controls. Evaluate CAPTCHA and alternatives such as a honeypot, submission timing checks, or risk-based challenges; select and document an approach based on effectiveness, accessibility, privacy, reliability, and user friction rather than requiring a specific vendor upfront. ## Scope - Registration form and Fortify server-side account creation flow. - Selection and implementation of a suitable spam prevention mechanism, which may combine multiple controls. - Direct registration POST requests as well as browser submissions. - Configuration, operational visibility, and any external verification service integration. - Existing registration throttling, validation, suspicious-registration auditing, and successful account provisioning. - Existing spam account cleanup and protections on unrelated forms are outside this issue. ## Acceptance Criteria - Document the selected approach and why it fits registration spam prevention, including accessibility, privacy, operating cost, and service availability considerations. - Enforce protection on the server before creating an account or related records, provisioning a Fider user, or triggering registration delivery side effects; direct POST requests cannot bypass it. - Reject submissions that fail the selected checks with a clear, accessible message and a practical retry path for legitimate users. - If a challenge/token service is selected, verify submissions server-side and handle missing, invalid, expired, and replayed tokens according to the provider's verification contract. - Define and test behavior for verification timeouts, provider failures, missing configuration, and blocked challenge scripts; failures must not silently disable protection. - Preserve existing username/email validation, password requirements, IP throttling, suspicious-registration auditing, and successful registration behavior. - Support the existing responsive layout, light/dark themes, keyboard navigation, and form error handling. Never repopulate passwords after an error. - Keep secrets in environment-backed configuration and out of source control, client markup, and logs; document setup and rollback. Any new dependency requires approval under repository policy. - Provide enough operational visibility to evaluate blocked attempts and false positives without logging passwords, secrets, or challenge tokens. ## Test Coverage Required - Extend `tests/Feature/Auth/RegistrationTest.php` or add focused registration spam prevention coverage. - Cover legitimate registration and rejection of bot-like or failed-verification submissions, including direct POST attempts. - Assert rejected attempts create no user or associated account records and trigger no external provisioning or registration delivery side effects. - Cover missing/invalid/expired/replayed challenge tokens where applicable, plus provider errors, timeouts, and configuration failures using fakes without live network calls. - Preserve existing registration throttling and validation coverage and `tests/Feature/SuspiciousRegistrationTest.php` behavior. - Verify accessible errors, retry behavior, and registration form rendering for the selected mechanism. - Run focused registration and suspicious-registration tests with the testing environment before closing the issue. - Run `vendor/bin/pint --dirty --format agent` and `composer stan` if PHP changes; run `npm run build` if shared frontend assets change. ## Progress Checklist - [x] Evaluate CAPTCHA and alternative controls and document the selected approach - [x] Define configuration, failure behavior, accessibility, and rollout/rollback requirements - [x] Implement server-side prevention before account creation and registration side effects - [x] Add any required accessible form controls and retry/error states - [x] Preserve existing throttling, validation, auditing, and provisioning behavior - [x] Add focused spam prevention and registration regression coverage - [x] Verify desktop/mobile, keyboard access, and light/dark themes - [x] Run focused registration and suspicious-registration tests - [x] Run Pint, PHPStan, and the asset build where applicable - [x] Document configuration, operational verification, and rollback - [x] Move the issue out of Codex review once verification is complete Issue structure follows #853. ## Acceptance review — November 5, 2026 Implementation and local verification meet the acceptance criteria and required test coverage. The checklist above records implementation completion; it does not claim production deployment or a live Cloudflare verification test. | Requirement | Implementation and evidence | | --- | --- | | Approach selection | The tracked README compares managed Turnstile, hCaptcha, reCAPTCHA, honeypots, and timing checks, including friction, accessibility, privacy, cost, availability, and limitations. Managed Turnstile was selected without adding dependencies. | | Server enforcement before side effects | `CreateNewUser` invokes `RegistrationSpamVerifier` after the existing throttle and account-field validation but before the account transaction, related records, suspicious-registration audit, Fider provisioning, and Fortify registration events. Direct POST and JSON requests cannot bypass verification. | | Token and provider contract | Strict success, action `register`, and an environment-backed exact hostname allowlist are required. Missing/malformed tokens, invalid/expired/replayed tokens, context mismatches, provider errors, malformed responses, redirects, timeouts, and missing configuration fail closed. HTTP requests use the fixed Siteverify endpoint, three-second connection and five-second overall timeouts, with no automatic retry. Siteverify owns single-use and expiry enforcement. | | Accessible form and recovery | The actual Blade form is tested at 320px and 1280px in light/dark themes. Keyboard retry, live error/status messages, expiration, interaction failure, blocked scripts, a stalled script timeout, compact/flexible sizing, disabled JavaScript, and real Livewire navigation are covered. Removed-widget callbacks cannot restore stale tokens. A failed verification followed by a fresh-token registration succeeds. | | Existing behavior preserved | Registration validation, password requirements, three-attempts-per-IP-per-hour throttling, restored Cloudflare visitor IPs, account-related records, Fider behavior, and suspicious-registration auditing retain focused regression coverage. | | No rejected-registration side effects | Negative feature tests compare counts across users, all associated account tables, and activity logs, assert no registration event or notification/security-alert delivery, and prohibit Fider or other outbound provisioning requests. | | Privacy, observability, setup, and rollback | Environment-backed keys and hostname configuration, fixed sanitized outcome/reason logs, Sentry registration URL and request-metadata minimization, and exclusion of passwords/tokens from restored form input are covered. The README documents setup, privacy/network requirements, failure behavior, operational checks, and rollback. | Verification completed on the local `codex/issue-999-registration-spam-prevention` branch: - Focused registration, spam prevention, email verification, suspicious-registration, and visitor-IP tests: **129 passed**, using `--env=testing` and provider fakes. - Full PHP suite: **1,549 passed, 2 skipped** (1,551 total), using `--env=testing -d memory_limit=1G`. The default 128 MB CLI limit was insufficient for full-suite discovery. Existing duplicate-constant warnings in unrelated tests remain; the clean sequential full run has no failures. - Playwright registration, email verification, and shared-JavaScript checks: **20 passed**, using local fixtures and a fake provider, with no live verification requests. - `vendor/bin/pint --dirty --format agent`, explicit Pint checks for new PHP files, `composer stan`, `npm run build`, and `git diff --check`: **passed**. The administrative checklist condition is already satisfied: the issue is labeled `status/in progress` and has no Codex-review status to remove. It remains open and assigned to Codex. Changes remain local and uncommitted; no PR or deployment has been created. Production environment values and a live widget/registration check remain deployment-time verification steps. ### Follow-up acceptance review — November 5, 2026 A second review found and corrected a diagnostic privacy gap: Sentry captures the full request URL and query string independently of the request body. The original body filter could therefore retain a password or challenge token submitted in a registration query string; referrer headers could also repeat those values. Registration request diagnostics now retain only the query-free URL and HTTP method, excluding bodies, query strings, headers, and cookies. Matching follows Laravel's decoded/trailing-slash-normalized path and honors Fortify's configured registration path and prefix. Unrelated request diagnostics retain their existing behavior. The new regression initially failed against the old implementation. Eight added cases now pass across Sentry events/transactions and canonical, trailing-slash, encoded, and configured registration paths. The README documents this policy. Fresh verification: **127 focused tests passed**, **1,547 full-suite tests passed with 2 skipped**, Pint, PHPStan, and whitespace checks passed. The full suite used the temporary 1 GB test-process memory limit and ran sequentially. Frontend assets and behavior were unchanged in this follow-up; the previously recorded 20 browser checks and successful asset build remain applicable. All 11 implementation checklist items remain complete. No further implementation gaps were found. The issue remains open and in progress; no PR or deployment was created, and live Cloudflare verification remains a deployment-time check. ### Stack-trace privacy acceptance review — November 5, 2026 An additional review reproduced another diagnostic leak using the installed Sentry SDK's real stack-trace builder: registration input arrays were retained as frame argument values, including passwords and challenge tokens. Filtering request metadata alone does not remove those arguments. `RegistrationDiagnostics` now clears argument values from the event's primary stack trace and every chained exception stack trace for registration requests, preserving the frames and their file/line/function information. Unrelated-route stack traces keep their existing argument behavior. The README documents this policy. The regression failed before the fix and passes for both event and transaction callbacks afterward. Fresh verification: **129 focused PHP tests passed**; the sequential full suite passed **1,549 tests with 2 skipped** (1,551 total) using the temporary 1 GB test-process memory limit. Pint, PHPStan, and whitespace checks passed. No frontend behavior or assets changed in this review, so the previously recorded 20 passing browser checks and successful asset build remain applicable. All 11 implementation checklist items remain complete, with no other concrete gaps found against the issue's description, acceptance criteria, and required coverage. The issue remains open and in progress. No PR, deployment, dependency change, or production-data change was made; live Cloudflare verification remains a deployment-time check.
Codex self-assigned this 2026-11-05 10:39:31 -06:00
Author
Member

Merged in PR #1015 as 15d76c01d5fafc587b6ee33c20a89ffdc6ed76e8 (implementation commit 2b1dc55c). All 11 implementation checklist items are complete.

Registration now requires managed Turnstile verified server-side before account creation, related records, Fider provisioning, or registration delivery. Existing validation, throttling, auditing, and provisioning remain covered. The form supports accessible retries, expiration/failure recovery, responsive sizing, themes, and Livewire navigation. Registration diagnostics exclude sensitive request metadata and stack arguments. Verification-email resends are limited per account to one per minute and five per hour; verification links remain challenge-free.

Verification: 129 focused PHP tests and 20 browser checks passed. The latest full PHP suite passed 1,549 tests with 2 skipped using a temporary 1 GB process memory limit. Pint, PHPStan, asset build, and whitespace checks passed. No migrations or new dependencies are required for this change.

Deployment notes: configure TURNSTILE_SITE_KEY, TURNSTILE_SECRET_KEY, and TURNSTILE_ALLOWED_HOSTNAMES independently in each environment before rollout. Use Managed mode with pre-clearance disabled. Verification fails closed when configuration or the provider is unavailable. Rebuild application configuration and assets, then check the live registration widget. Setup, operations, and rollback are documented in README.md.

Closing as requested. Dev and production deployment is starting; verified deployment results and backup locations will follow in a separate comment. No production deployment is claimed by this closeout note.

Merged in PR #1015 as `15d76c01d5fafc587b6ee33c20a89ffdc6ed76e8` (implementation commit `2b1dc55c`). All 11 implementation checklist items are complete. Registration now requires managed Turnstile verified server-side before account creation, related records, Fider provisioning, or registration delivery. Existing validation, throttling, auditing, and provisioning remain covered. The form supports accessible retries, expiration/failure recovery, responsive sizing, themes, and Livewire navigation. Registration diagnostics exclude sensitive request metadata and stack arguments. Verification-email resends are limited per account to one per minute and five per hour; verification links remain challenge-free. Verification: 129 focused PHP tests and 20 browser checks passed. The latest full PHP suite passed 1,549 tests with 2 skipped using a temporary 1 GB process memory limit. Pint, PHPStan, asset build, and whitespace checks passed. No migrations or new dependencies are required for this change. Deployment notes: configure `TURNSTILE_SITE_KEY`, `TURNSTILE_SECRET_KEY`, and `TURNSTILE_ALLOWED_HOSTNAMES` independently in each environment before rollout. Use Managed mode with pre-clearance disabled. Verification fails closed when configuration or the provider is unavailable. Rebuild application configuration and assets, then check the live registration widget. Setup, operations, and rollback are documented in README.md. Closing as requested. Dev and production deployment is starting; verified deployment results and backup locations will follow in a separate comment. No production deployment is claimed by this closeout note.
Author
Member

Deployed PR #1015 to dev and production from merged source revision 15d76c01d5fafc587b6ee33c20a89ffdc6ed76e8.

The established checksum-guarded patch procedure applied 16 runtime files/assets. Config, route, and view caches were rebuilt. Deployment receipts append +pr1015-15d76c01 to each environment's existing release history, accurately preserving its prior patched-release lineage. Unrelated runtime files, dependencies, lockfiles, existing assets, and environment settings were preserved. No migrations or account/domain-data writes were performed.

Dev was missing the Turnstile settings. Only the three Turnstile values already configured in the workstation's .env.local were applied to the dev environment, with its original environment file backed up privately. Production used its existing settings. No keys or secrets are included in these notes.

Verified on both environments:

  • Correct environment/database target and deployed file checksums.
  • Turnstile configuration readiness and allowed hostname coverage.
  • Registration rendering without exposing the secret key; current named verification throttling in the route cache.
  • Home, login, registration, and both versioned JS/CSS assets return HTTP 200.
  • The live Cloudflare widget renders in the browser without page JavaScript errors.
  • Updated release manifests and maintenance mode disabled after deployment.

The managed widget requested human checkbox interaction in the automated browser. That challenge was not automated, no registration was submitted, and no email was sent. A human-completed end-to-end registration remains the final manual smoke test; no successful live Siteverify token exchange is claimed.

Backups:

  • Dev code/assets: /home/mvgldev/deploy-backups/pr1015-15d76c01-olmgo7jr
  • Production code/assets: /home/mvgl/deploy-backups/pr1015-15d76c01-j1zkv2u7
  • Dev environment before the three-value update: /home/mvgldev/deploy-backups/pr1015-dev-config-1tvvcl26

The guarded deployment/rollback runner is ~/deploy-stage/pr1015-15d76c01/deploy.py under each respective account. To roll back code/assets, run it with rollback <the-account-specific-backup-path> from the application directory; it restores previous files/release metadata and refreshes caches. Reverting removes preventative challenge enforcement and should be an explicit operational decision. The private dev environment backup is separate.

Issue #999 remains closed with status/done and all 11 implementation checklist items checked.

Deployed PR #1015 to **dev and production** from merged source revision `15d76c01d5fafc587b6ee33c20a89ffdc6ed76e8`. The established checksum-guarded patch procedure applied 16 runtime files/assets. Config, route, and view caches were rebuilt. Deployment receipts append `+pr1015-15d76c01` to each environment's existing release history, accurately preserving its prior patched-release lineage. Unrelated runtime files, dependencies, lockfiles, existing assets, and environment settings were preserved. No migrations or account/domain-data writes were performed. Dev was missing the Turnstile settings. Only the three Turnstile values already configured in the workstation's `.env.local` were applied to the dev environment, with its original environment file backed up privately. Production used its existing settings. No keys or secrets are included in these notes. Verified on both environments: - Correct environment/database target and deployed file checksums. - Turnstile configuration readiness and allowed hostname coverage. - Registration rendering without exposing the secret key; current named verification throttling in the route cache. - Home, login, registration, and both versioned JS/CSS assets return HTTP 200. - The live Cloudflare widget renders in the browser without page JavaScript errors. - Updated release manifests and maintenance mode disabled after deployment. The managed widget requested human checkbox interaction in the automated browser. That challenge was not automated, no registration was submitted, and no email was sent. A human-completed end-to-end registration remains the final manual smoke test; no successful live Siteverify token exchange is claimed. Backups: - Dev code/assets: `/home/mvgldev/deploy-backups/pr1015-15d76c01-olmgo7jr` - Production code/assets: `/home/mvgl/deploy-backups/pr1015-15d76c01-j1zkv2u7` - Dev environment before the three-value update: `/home/mvgldev/deploy-backups/pr1015-dev-config-1tvvcl26` The guarded deployment/rollback runner is `~/deploy-stage/pr1015-15d76c01/deploy.py` under each respective account. To roll back code/assets, run it with `rollback <the-account-specific-backup-path>` from the application directory; it restores previous files/release metadata and refreshes caches. Reverting removes preventative challenge enforcement and should be an explicit operational decision. The private dev environment backup is separate. Issue #999 remains closed with `status/done` and all 11 implementation checklist items checked.
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MyVideoGameList/myvideogamelist.com#999
No description provided.