Skip to content

Improve rule verification flow & add logs#10962

Merged
hmacr merged 5 commits into1.8.xfrom
ser-721
Dec 16, 2025
Merged

Improve rule verification flow & add logs#10962
hmacr merged 5 commits into1.8.xfrom
ser-721

Conversation

@hmacr
Copy link
Copy Markdown
Contributor

@hmacr hmacr commented Dec 16, 2025

Related SER-721

Along the way, refactored the code to use common constants & action for rule verification.

@hmacr hmacr requested a review from Meldiron December 16, 2025 05:32
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Dec 16, 2025

📝 Walkthrough

Walkthrough

This PR adds a logs attribute to the webhooks collection and introduces four RULE_STATUS_* constants. It replaces single-server DNS validation with a multi-server, coroutine-based validator and adds a new Action class that centralizes domain restriction and verification logic (CAA/CNAME/A/AAAA target resolution driven by environment). Multiple proxy rule handlers (Create/Get/Function/Site/Redirect/Verification/XList) were refactored to use string-based domains, inject logging, update status transitions (CREATED → VERIFIED → CERTIFICATE_GENERATING), and call the new verification flow. It also adds CoreDNS test infrastructure, updates docker-compose and .env DNS settings, and expands E2E/unit tests and rule response metadata.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Heterogeneous, multi-area changes: DNS validator rewrite (concurrency + aggregation), new Action class (complex environment-driven validation), and multiple handler signature/flow changes.
  • Test and infrastructure updates (CoreDNS, docker-compose, .env) that affect CI/E2E runs.

Areas to pay extra attention:

  • DNS parallel validation correctness, coroutine/WaitGroup usage, and error propagation.
  • verifyRule: resolution of environment variables, composition of validators (AnyOf), and thrown exception messages.
  • Consistency and usage of new RULE_STATUS_* values across handlers, queuing, and tests.
  • Updated method signatures (added Log parameter and variadic constructors) and all call sites.
  • Docker-compose IPAM/CoreDNS configuration and Corefile correctness relative to tests.

Possibly related PRs

  • Feat: A/AAA record support #9627 — Modifies DNS validation and custom-domain verification logic; strong overlap with validator and proxy rule verification changes.

Suggested reviewers

  • eldadfux

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main changes: improving rule verification flow and adding logs functionality, which aligns with the substantial refactoring across multiple rule creation/verification files.
Description check ✅ Passed The description is related to the changeset, mentioning rule verification flow improvements and refactoring to use common constants and actions, which matches the substantial changes across rule-related files.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ser-721

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@hmacr hmacr self-assigned this Dec 16, 2025
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php (1)

134-150: Rule status and logs changes are not persisted to the database.

After successful verification, the status is changed to RULE_STATUS_CERTIFICATE_GENERATING (line 137), and on failure, logs are set (line 139). However, these in-memory changes are never persisted back to the database via updateDocument. The response will show the updated status, but the database record will remain with RULE_STATUS_CREATED.

Add a database update after modifying the rule:

         if ($rule->getAttribute('status', '') === RULE_STATUS_CREATED) {
             try {
                 $this->verifyRule($rule, $log);
                 $rule->setAttribute('status', RULE_STATUS_CERTIFICATE_GENERATING);
             } catch (Exception $err) {
                 $rule->setAttribute('logs', $err->getMessage());
             }
+            $rule = $dbForPlatform->updateDocument('rules', $rule->getId(), $rule);
         }
src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php (1)

134-150: Rule status and logs changes are not persisted to the database.

After creating the rule at line 129, the status and logs attributes are modified on lines 137-139, but there's no updateDocument call to persist these changes. The response will show the updated values, but the database will retain the original state, causing data inconsistency.

Add an update call after the verification/certificate logic:

     if ($rule->getAttribute('status', '') === RULE_STATUS_CERTIFICATE_GENERATING) {
         $queueForCertificates
             ->setDomain(new Document([
                 'domain' => $rule->getAttribute('domain'),
                 'domainType' => $rule->getAttribute('deploymentResourceType', $rule->getAttribute('type')),
             ]))
             ->trigger();
     }
 
+    $rule = $dbForPlatform->updateDocument('rules', $rule->getId(), $rule);
+
     $queueForEvents->setParam('ruleId', $rule->getId());
🧹 Nitpick comments (4)
app/init/constants.php (1)

211-216: Consider adding clarifying comments for semantic alignment.

The constant names describe internal implementation details (certificate generation), while the values represent user-facing API states. This is likely intentional for API backward compatibility, but may cause confusion for developers.

Consider enhancing the comments to clarify the mapping:

 // Rule statuses
 const RULE_STATUS_CREATED = 'created'; // This is also the status when domain DNS verification fails.
-const RULE_STATUS_CERTIFICATE_GENERATING = 'verifying';
-const RULE_STATUS_CERTIFICATE_GENERATION_FAILED = 'unverified';
+const RULE_STATUS_CERTIFICATE_GENERATING = 'verifying'; // API value: 'verifying' - certificate issuance in progress
+const RULE_STATUS_CERTIFICATE_GENERATION_FAILED = 'unverified'; // API value: 'unverified' - certificate issuance failed
 const RULE_STATUS_VERIFIED = 'verified';
src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php (1)

81-160: Consider extracting common rule creation logic to reduce duplication.

The action methods in API/Create.php, Function/Create.php, and Redirect/Create.php share significant duplicate code for status determination, owner assignment, verification flow, and certificate queueing. Consider extracting this common logic into the base Action class or a shared trait.

src/Appwrite/Platform/Modules/Proxy/Http/Rules/Action.php (2)

75-79: Shadowed variable $domain is unused after validation.

The Domain object is created to validate the format, but it shadows the string $domain parameter and is never used. If the intent is purely format validation, the object is discarded correctly, but the variable shadowing could cause confusion in future maintenance.

Consider using a different variable name or an inline validation:

         try {
-            $domain = new Domain($domain);
+            new Domain($domain); // Validates domain format
         } catch (\Throwable) {
             throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Domain may not start with http:// or https://.');
         }

140-171: Redundant null checks for $mainValidator assignment.

The first validator added to the array will always satisfy the is_null($mainValidator) condition, making subsequent checks unnecessary. The pattern is repeated three times.

Simplify by assigning directly on the first validator:

         $validators = [];
-        $mainValidator = null; // Validator to use for error description

         if (!is_null($targetCNAME)) {
             $validator = new $dnsValidatorClass($targetCNAME->get(), Record::TYPE_CNAME, $dnsServers);
             $validators[] = $validator;
-
-            if (\is_null($mainValidator)) {
-                $mainValidator = $validator;
-            }
         }

         // Ensure at least one of CNAME/A/AAAA record points to our servers properly
         $targetA = System::getEnv('_APP_DOMAIN_TARGET_A', '');
         if ((new IP(IP::V4))->isValid($targetA)) {
             $validator = new $dnsValidatorClass($targetA, Record::TYPE_A, $dnsServers);
             $validators[] = $validator;
-
-            if (\is_null($mainValidator)) {
-                $mainValidator = $validator;
-            }
         }

         $targetAAAA = System::getEnv('_APP_DOMAIN_TARGET_AAAA', '');
         if ((new IP(IP::V6))->isValid($targetAAAA)) {
             $validator = new $dnsValidatorClass($targetAAAA, Record::TYPE_AAAA, $dnsServers);
             $validators[] = $validator;
-
-            if (\is_null($mainValidator)) {
-                $mainValidator = $validator;
-            }
         }

         if (empty($validators)) {
             throw new Exception(Exception::GENERAL_SERVER_ERROR, 'At least one of domain targets environment variable must be configured.');
         }

+        $mainValidator = $validators[0]; // Use first validator for error description
         $validator = new AnyOf($validators, AnyOf::TYPE_STRING);
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8cd000c and 5ae148f.

📒 Files selected for processing (12)
  • app/config/collections/platform.php (1 hunks)
  • app/init/constants.php (1 hunks)
  • src/Appwrite/Network/Validator/DNS.php (1 hunks)
  • src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php (5 hunks)
  • src/Appwrite/Platform/Modules/Proxy/Http/Rules/Action.php (1 hunks)
  • src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php (7 hunks)
  • src/Appwrite/Platform/Modules/Proxy/Http/Rules/Get.php (2 hunks)
  • src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php (7 hunks)
  • src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php (7 hunks)
  • src/Appwrite/Platform/Modules/Proxy/Http/Rules/Verification/Update.php (5 hunks)
  • src/Appwrite/Platform/Modules/Proxy/Http/Rules/XList.php (2 hunks)
  • src/Appwrite/Utopia/Response/Model/Rule.php (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-05-10T04:12:13.037Z
Learnt from: ItzNotABug
Repo: appwrite/appwrite PR: 9693
File: src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php:85-86
Timestamp: 2025-05-10T04:12:13.037Z
Learning: The class `Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Indexes\Action` defines a method `getGrantParentNotFoundException()` which is used to handle cases where a parent resource (like a database collection) is not found.

Applied to files:

  • src/Appwrite/Platform/Modules/Proxy/Http/Rules/Get.php
🧬 Code graph analysis (5)
src/Appwrite/Platform/Modules/Proxy/Http/Rules/XList.php (2)
src/Appwrite/Platform/Modules/Proxy/Http/Rules/Get.php (1)
  • __construct (24-54)
src/Appwrite/Platform/Modules/Proxy/Http/Rules/Delete.php (1)
  • __construct (28-62)
src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php (1)
src/Appwrite/Platform/Modules/Proxy/Http/Rules/Action.php (4)
  • Action (17-188)
  • __construct (19-21)
  • validateDomainRestrictions (31-80)
  • verifyRule (89-187)
src/Appwrite/Network/Validator/DNS.php (1)
src/Appwrite/Platform/Modules/Proxy/Http/Rules/Action.php (1)
  • __construct (19-21)
src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php (1)
src/Appwrite/Platform/Modules/Proxy/Http/Rules/Action.php (3)
  • Action (17-188)
  • validateDomainRestrictions (31-80)
  • verifyRule (89-187)
src/Appwrite/Platform/Modules/Proxy/Http/Rules/Get.php (1)
src/Appwrite/Utopia/Response/Model/Rule.php (1)
  • __construct (10-107)
🪛 PHPMD (2.15.0)
src/Appwrite/Platform/Modules/Proxy/Http/Rules/Verification/Update.php

75-75: Avoid unused parameters such as '$platform'. (undefined)

(UnusedFormalParameter)

🔇 Additional comments (13)
app/config/collections/platform.php (1)

1320-1330: LGTM!

The new logs attribute for the rules collection follows the established pattern and is consistent with similar logs fields in the certificates (line 1034) and webhooks (line 947) collections. The 1MB size limit is appropriate for log storage.

src/Appwrite/Utopia/Response/Model/Rule.php (1)

93-98: LGTM!

The updated description accurately documents the log prioritization behavior (certificate logs take precedence), and the example provides a realistic DNS verification failure scenario that helps API consumers understand the expected log format.

src/Appwrite/Platform/Modules/Proxy/Http/Rules/XList.php (2)

29-31: LGTM!

The constructor pattern aligns with the Get.php implementation, properly forwarding parameters to the parent class.


113-117: LGTM!

The conditional log handling correctly prioritizes certificate generation logs over rule verification logs when present, matching the documented behavior in the Rule model and consistent with the Get.php implementation.

src/Appwrite/Platform/Modules/Proxy/Http/Rules/Get.php (2)

24-27: LGTM!

Constructor properly forwards parameters to parent, consistent with the pattern in XList.php.


69-73: LGTM!

The conditional log prioritization logic is consistent with XList.php and correctly implements the documented behavior where certificate logs take precedence when available.

src/Appwrite/Platform/Modules/Proxy/Http/Rules/Verification/Update.php (2)

86-89: Verify early return doesn't skip event emission.

The early return when status is RULE_STATUS_VERIFIED or RULE_STATUS_CERTIFICATE_GENERATING means queueForEvents is set with ruleId (line 84) but no event is triggered or explicitly skipped. Verify this is the intended behavior.


94-97: This pattern is correct. The updateDocument() method in Utopia Database is designed for partial updates—passing a Document with only the fields to update (logs, status, $updatedAt) merges those fields with the existing document as intended. This is the standard and idiomatic approach used consistently throughout the Appwrite codebase. No changes needed.

src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php (1)

33-35: LGTM!

The variadic constructor pattern properly forwards parameters to the parent Action class, enabling dependency injection for the DNS validator class.

src/Appwrite/Platform/Modules/Proxy/Http/Rules/Action.php (1)

89-186: LGTM on the overall verification flow.

The verifyRule method correctly validates CAA records, supports multiple DNS record types (CNAME, A, AAAA), uses configurable DNS servers, and provides detailed logging for debugging DNS timing issues. The use of AnyOf validator allows flexible DNS configurations.

src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php (3)

33-36: LGTM on constructor changes.

The variadic parameter forwarding to the parent constructor is clean and allows the base Action class to handle optional dependencies like dnsValidatorClass.


143-150: Confirm that Appwrite-managed domains should skip certificate generation.

When a domain ends with functionsDomain or sitesDomain, the status is set to RULE_STATUS_VERIFIED (line 105), which bypasses both the verification block (line 134) and the certificate queue (line 143).

Please confirm this is intentional—presumably these domains are covered by wildcard certificates.


103-106: No changes needed—rule status constants are properly defined and accessible.

The constants RULE_STATUS_CREATED, RULE_STATUS_VERIFIED, and RULE_STATUS_CERTIFICATE_GENERATING are properly defined as global constants in app/init/constants.php, which is loaded during application bootstrap. No explicit imports are required for global PHP constants.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Dec 16, 2025

Security Scan Results for PR

Docker Image Scan Results

Package Version Vulnerability Severity
libpng 1.6.51-r0 CVE-2025-66293 HIGH
libpng-dev 1.6.51-r0 CVE-2025-66293 HIGH

Source Code Scan Results

🎉 No vulnerabilities found!

@github-actions
Copy link
Copy Markdown

github-actions bot commented Dec 16, 2025

✨ Benchmark results

  • Requests per second: 1,062
  • Requests with 200 status code: 191,209
  • P99 latency: 0.18230272

⚡ Benchmark Comparison

Metric This PR Latest version
RPS 1,062 1,155
200 191,209 207,911
P99 0.18230272 0.176928947

@hmacr hmacr changed the title Add logs to rules Improve rule verification flow & add logs Dec 16, 2025
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (1)
src/Appwrite/Platform/Modules/Proxy/Http/Rules/Verification/Update.php (1)

119-121: Potential undefined variable $certificate.

The variable $certificate is only assigned inside the if (!empty($certificateId)) block (line 100). If $certificateId is empty, accessing $certificate on line 119 will cause an undefined variable error in PHP 8+.

Apply this fix:

         $certificateId = $rule->getAttribute('certificateId', '');
+        $certificate = null;
         // Reset logs for the associated certificate.
         if (!empty($certificateId)) {
             $certificate = $dbForPlatform->updateDocument('certificates', $certificateId, new Document([
                 'logs' => '',
             ]));
         }
     } catch (Exception $err) {
         ...
     }

-    if (!empty($certificate)) {
+    if ($certificate !== null) {
         $rule->setAttribute('renewAt', $certificate->getAttribute('renewDate', ''));
     }
🧹 Nitpick comments (4)
tests/resources/coredns/Corefile (3)

1-4: Consider documenting the duplicate forward directive.

The global forward at line 3 and the zone-specific forward at line 63 both target 1.1.1.1. While zone-specific forwards can override global behavior in CoreDNS, this duplication may appear redundant. If intentional, consider adding a comment explaining why both are needed.

Also applies to: 63-63


10-10: Consider using RFC 5737 reserved IP ranges for test records.

The IP addresses 203.0.0.1 and 203.0.0.5 are not within the RFC 5737 reserved ranges for documentation and testing (192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24). Using reserved ranges helps avoid potential conflicts with real IPs and clearly signals that these are test values.

Example replacement:

-        answer "{{ .Name }} 60 IN A 203.0.0.1"
+        answer "{{ .Name }} 60 IN A 203.0.113.1"

Also applies to: 40-40, 53-53, 70-70


1-73: Consider adding inline comments to document test scenarios.

While the naming conventions (e.g., "stage-wrong-caa", "stage-correct-caa") are self-explanatory, inline comments would improve maintainability by explicitly documenting the purpose of each template and the specific validation scenarios they're designed to test.

Example:

+    # Test case: Validate A record resolution for base domain
     template IN A {
         match "^webapp\.com\.$"
         answer "{{ .Name }} 60 IN A 203.0.0.1"
         fallthrough
     }
tests/unit/Network/Validators/DNSTest.php (1)

11-27: Consider adding positive validation tests for more complete coverage.

The tests verify that empty/null inputs fail validation, but there are no tests asserting successful validation when a domain correctly resolves to the expected target. This limits confidence that the multi-server coroutine-based validation actually works for valid scenarios.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5ae148f and 1099679.

📒 Files selected for processing (9)
  • docker-compose.yml (4 hunks)
  • src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php (4 hunks)
  • src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php (6 hunks)
  • src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php (6 hunks)
  • src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php (6 hunks)
  • src/Appwrite/Platform/Modules/Proxy/Http/Rules/Verification/Update.php (3 hunks)
  • tests/e2e/Services/Proxy/ProxyCustomServerTest.php (6 hunks)
  • tests/resources/coredns/Corefile (1 hunks)
  • tests/unit/Network/Validators/DNSTest.php (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (6)
src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php (2)
src/Appwrite/Platform/Modules/Proxy/Http/Rules/Action.php (4)
  • Action (17-188)
  • __construct (19-21)
  • validateDomainRestrictions (31-80)
  • verifyRule (89-187)
src/Appwrite/Utopia/Response/Model/Rule.php (1)
  • __construct (10-107)
src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php (1)
src/Appwrite/Platform/Modules/Proxy/Http/Rules/Action.php (3)
  • Action (17-188)
  • validateDomainRestrictions (31-80)
  • verifyRule (89-187)
src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php (1)
src/Appwrite/Platform/Modules/Proxy/Http/Rules/Action.php (4)
  • Action (17-188)
  • __construct (19-21)
  • validateDomainRestrictions (31-80)
  • verifyRule (89-187)
src/Appwrite/Platform/Modules/Proxy/Http/Rules/Verification/Update.php (3)
src/Appwrite/Platform/Modules/Proxy/Http/Rules/Action.php (3)
  • Action (17-188)
  • __construct (19-21)
  • verifyRule (89-187)
src/Appwrite/Network/Validator/DNS.php (1)
  • __construct (20-25)
src/Appwrite/Event/Certificate.php (1)
  • setDomain (29-34)
src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php (1)
src/Appwrite/Platform/Modules/Proxy/Http/Rules/Action.php (4)
  • Action (17-188)
  • __construct (19-21)
  • validateDomainRestrictions (31-80)
  • verifyRule (89-187)
tests/unit/Network/Validators/DNSTest.php (1)
src/Appwrite/Network/Validator/DNS.php (2)
  • DNS (9-67)
  • isValid (33-66)
🪛 GitHub Actions: Linter
tests/unit/Network/Validators/DNSTest.php

[error] 1-1: PSR-12: single_blank_line_at_eof issue detected. Fix end-of-file newline. (rule: single_blank_line_at_eof). Command: 'vendor/bin/pint --test --config pint.json' failed with exit code 1.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (19)
  • GitHub Check: E2E Service Test (Databases/TablesDB)
  • GitHub Check: E2E Service Test (Locale)
  • GitHub Check: E2E Service Test (GraphQL)
  • GitHub Check: E2E Service Test (FunctionsSchedule)
  • GitHub Check: E2E Service Test (Storage)
  • GitHub Check: E2E Service Test (Proxy)
  • GitHub Check: E2E Service Test (Sites)
  • GitHub Check: E2E Service Test (Databases/Legacy)
  • GitHub Check: E2E Service Test (Avatars)
  • GitHub Check: E2E Service Test (Account)
  • GitHub Check: E2E Service Test (Functions)
  • GitHub Check: E2E Service Test (Health)
  • GitHub Check: E2E Service Test (Console)
  • GitHub Check: Unit Test
  • GitHub Check: E2E Service Test (Site Screenshots)
  • GitHub Check: E2E General Test
  • GitHub Check: E2E Service Test (Dev Keys)
  • GitHub Check: Benchmark
  • GitHub Check: scan
🔇 Additional comments (20)
tests/unit/Network/Validators/DNSTest.php (1)

29-40: LGTM!

The test correctly verifies that validation failure exposes the expected public properties (count, value, records) and getDescription() returns meaningful output. Using uniqid() ensures the domain doesn't exist.

docker-compose.yml (2)

1085-1098: LGTM! CoreDNS service configuration is well-structured for testing.

The service is properly isolated with a fixed IP, read-only volume mount for the Corefile, and restart policy. The commented-out port forwarding is a nice touch for debugging flexibility.


1230-1232: Network IPAM configuration correctly supports the fixed CoreDNS IP.

The subnet 172.16.238.0/24 properly encompasses the CoreDNS fixed address 172.16.238.100.

src/Appwrite/Platform/Modules/Proxy/Http/Rules/Verification/Update.php (2)

29-31: Constructor pattern aligns with parent class.

The variadic parameter forwarding to parent constructor is consistent with the Action base class pattern used across the codebase.


84-87: Early return logic correctly prevents redundant verification.

Returning early for RULE_STATUS_VERIFIED and RULE_STATUS_CERTIFICATE_GENERATING states prevents unnecessary re-processing of already verified or in-progress rules.

tests/e2e/Services/Proxy/ProxyCustomServerTest.php (2)

573-705: Comprehensive test coverage for rule verification scenarios.

The test methodically covers site rules, function rules, apex domains, CNAME records, CAA records, and various failure modes. This provides good coverage for the new verification flow.


707-738: Good test for timestamp update behavior on re-verification.

The test correctly validates that re-running verification updates the timestamp while preserving logs, which is important for audit and debugging purposes.

src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php (2)

103-106: Status check now correctly guards against empty domain strings.

The condition properly checks $functionsDomain != '' and $sitesDomain != '' before calling str_ends_with(), addressing the previous review concern about empty strings matching any domain.


128-135: Verification flow with error logging is well-structured.

The try-catch block properly attempts verification and captures error messages in logs on failure, allowing the rule to be created even when verification fails initially.

src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php (2)

85-91: Status and owner check correctly guards against empty domain strings.

The condition properly validates that environment variables are non-empty before using str_ends_with(), preventing false matches when $functionsDomain or $sitesDomain are empty.


107-114: Verification flow is consistent with other Create handlers.

The try-catch pattern for verification with error logging matches the implementation in Function/Create.php, maintaining consistency across rule creation paths.

src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php (5)

8-8: LGTM: Structural changes align with refactoring goals.

The addition of Action base class import, Log injection, and variadic constructor properly support the new centralized verification flow with logging capabilities.

Also applies to: 18-18, 34-36, 77-77


102-108: Empty string guard issue from previous review is now resolved.

The current code properly guards against empty environment variables with $functionsDomain != '' and $sitesDomain != '' checks before calling str_ends_with(). The short-circuit && evaluation ensures that str_ends_with($domain, '') is never evaluated when the environment variable is empty, preventing the status from being incorrectly set to VERIFIED.


129-136: Persistence issue from previous review is now resolved.

The modifications to status and logs are correctly persisted. The code modifies the $rule Document object in-memory (lines 132, 134), then calls $dbForPlatform->createDocument('rules', $rule) at line 139, which persists all attributes including the verification changes. This is the standard creation pattern in Utopia Database.

The verification flow is well-structured:

  • On successful verification: status transitions to CERTIFICATE_GENERATING
  • On failure: error message captured in logs, status remains CREATED

81-83: LGTM: Improved observability and centralized validation.

The addition of the Log parameter enables DNS timing metrics and diagnostic logging during verification. Delegating domain validation to validateDomainRestrictions() centralizes the domain restriction logic and reduces code duplication across rule types.


98-99: LGTM: Clean domain handling and clear status lifecycle.

The shift to string-based domain handling simplifies the codebase. The new RULE_STATUS_* constants make the status lifecycle explicit:

  • CREATED: Initial state for external domains requiring verification
  • VERIFIED: For Appwrite-owned domains (*.sites or *.functions) that skip verification
  • CERTIFICATE_GENERATING: Triggered only after successful verification

The certificate generation is correctly gated on the CERTIFICATE_GENERATING status, ensuring certificates are only issued for verified domains.

Also applies to: 114-114, 124-124, 144-144

src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php (4)

8-8: LGTM: Consistent refactoring across rule types.

The structural changes mirror those in Redirect/Create.php, maintaining consistency across the rule creation handlers. The Action base class extension and Log injection enable the centralized verification flow with observability.

Also applies to: 18-18, 33-35, 74-74


97-103: Empty string guards properly implemented.

The guards $functionsDomain != '' and $sitesDomain != '' correctly prevent str_ends_with($domain, '') from being evaluated with empty environment variables, avoiding the incorrect VERIFIED status assignment.


125-132: Verification flow correctly implemented.

The rule modifications are properly persisted through createDocument() at line 135. The verification logic correctly:

  • Attempts verification for CREATED status rules
  • Transitions to CERTIFICATE_GENERATING on success
  • Captures error messages in logs on failure while maintaining CREATED status for retry attempts

78-78: LGTM: Consistent implementation with site-specific enhancements.

The implementation follows the same refactored patterns as Redirect/Create.php:

  • Centralized domain validation via validateDomainRestrictions()
  • String-based domain handling
  • Clear status lifecycle with RULE_STATUS_* constants
  • Log-enabled verification for observability

The inclusion of the $branch parameter in the search field (line 120) is appropriate for site rules that support VCS-based deployments.

Also applies to: 80-80, 93-94, 109-109, 120-120, 140-140

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
tests/unit/Network/Validators/DNSTest.php (2)

42-52: CoreDNS dependency requires skip logic or integration test placement.

This test depends on CoreDNS running at 172.16.238.100 with no mechanism to skip when unavailable. Consider adding a connectivity check with markTestSkipped() or moving to an integration test suite.


52-53: Add missing newline at end of file.

PSR-12 requires files to end with a single blank line.

     }
 }
+
🧹 Nitpick comments (2)
tests/unit/Network/Validators/DNSTest.php (2)

11-18: Tests lack success case validation.

Both testSingleDNSServer and testMultipleDNSServers only verify failure scenarios (empty/null inputs). Consider adding assertions for successful validation where a domain correctly resolves to the expected target to actually exercise the validation logic.

Additionally, these two tests have identical assertions—consider differentiating them by testing behavior unique to single vs. multi-server validation (e.g., partial failure scenarios).


20-27: No explicit test for multi-server consensus behavior.

The DNS validator's core multi-server feature (fail if ANY server disagrees) isn't directly tested. The CoreDNS setup in testCoreDNSFailure partially addresses this, but consider a test where servers are expected to return differing results to verify the fail-fast behavior documented in the implementation.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1099679 and 49b1c3d.

📒 Files selected for processing (5)
  • .env (1 hunks)
  • docker-compose.yml (4 hunks)
  • src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php (4 hunks)
  • tests/e2e/Services/Proxy/ProxyCustomServerTest.php (6 hunks)
  • tests/unit/Network/Validators/DNSTest.php (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • docker-compose.yml
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-08T01:20:14.364Z
Learnt from: stnguyen90
Repo: appwrite/appwrite PR: 10119
File: app/controllers/api/account.php:1226-1232
Timestamp: 2025-07-08T01:20:14.364Z
Learning: In Appwrite, `_APP_DOMAIN` is a required environment variable that must always be set for the system to function properly.

Applied to files:

  • .env
🧬 Code graph analysis (2)
tests/e2e/Services/Proxy/ProxyCustomServerTest.php (3)
tests/e2e/General/UsageTest.php (1)
  • tearDown (1380-1383)
tests/e2e/Services/Sites/SitesBase.php (2)
  • setupSite (21-34)
  • cleanupSite (85-94)
tests/e2e/Services/Functions/FunctionsBase.php (2)
  • setupFunction (20-33)
  • cleanupFunction (69-78)
tests/unit/Network/Validators/DNSTest.php (1)
src/Appwrite/Network/Validator/DNS.php (2)
  • DNS (9-67)
  • isValid (33-66)
🪛 dotenv-linter (4.0.0)
.env

[warning] 24-24: [UnorderedKey] The _APP_DNS key should go before the _APP_EDITION key

(UnorderedKey)


[warning] 24-24: [ValueWithoutQuotes] This value needs to be surrounded in quotes

(ValueWithoutQuotes)


[warning] 25-25: [UnorderedKey] The _APP_DOMAIN key should go before the _APP_EDITION key

(UnorderedKey)


[warning] 26-26: [UnorderedKey] The _APP_CONSOLE_DOMAIN key should go before the _APP_CONSOLE_HOSTNAMES key

(UnorderedKey)


[warning] 27-27: [UnorderedKey] The _APP_DOMAIN_FUNCTIONS key should go before the _APP_EDITION key

(UnorderedKey)


[warning] 28-28: [UnorderedKey] The _APP_DOMAIN_SITES key should go before the _APP_EDITION key

(UnorderedKey)


[warning] 29-29: [UnorderedKey] The _APP_DOMAIN_TARGET_CNAME key should go before the _APP_EDITION key

(UnorderedKey)


[warning] 30-30: [UnorderedKey] The _APP_DOMAIN_TARGET_A key should go before the _APP_DOMAIN_TARGET_CNAME key

(UnorderedKey)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (13)
  • GitHub Check: E2E Service Test (Tokens)
  • GitHub Check: E2E Service Test (FunctionsSchedule)
  • GitHub Check: E2E Service Test (Functions)
  • GitHub Check: E2E Service Test (Console)
  • GitHub Check: E2E Service Test (Databases/Legacy)
  • GitHub Check: E2E Service Test (Account)
  • GitHub Check: E2E Service Test (Databases/TablesDB)
  • GitHub Check: E2E Service Test (Dev Keys)
  • GitHub Check: E2E Service Test (Site Screenshots)
  • GitHub Check: Unit Test
  • GitHub Check: E2E General Test
  • GitHub Check: Benchmark
  • GitHub Check: scan
🔇 Additional comments (13)
tests/unit/Network/Validators/DNSTest.php (1)

29-40: LGTM!

Good use of \uniqid() to generate unique domains avoiding DNS caching, and proper verification of public properties exposed after validation failure.

.env (2)

24-24: DNS configuration updated correctly for CoreDNS integration.

The IP address 172.16.238.100 correctly points to the CoreDNS service as indicated in the docker-compose.yml changes mentioned in the AI summary.

Optional style note: The static analysis tool suggests surrounding the value in quotes ("172.16.238.100"), though this is not required for .env files to function correctly.


29-30: Domain target updates align with CoreDNS test infrastructure.

The CNAME and A record target changes support the new multi-server DNS validation approach referenced in the AI summary and are consumed by the verification tests.

tests/e2e/Services/Proxy/ProxyCustomServerTest.php (4)

18-46: Well-implemented tearDown with correct query serialization.

The cleanup logic properly deletes test rules and verifies completion. The previous issue with Query::limit(1) missing ->toString() on line 39 has been correctly resolved.


89-89: Excellent refactoring to use status constants.

Replacing string literals with RULE_STATUS_CREATED and RULE_STATUS_VERIFIED constants improves maintainability and reduces the risk of typos.

Also applies to: 391-391, 400-400, 409-409


573-705: Comprehensive verification test coverage.

This test method provides excellent coverage of the rule verification flow, including:

  • Successful verifications (site, function, API with correct DNS records)
  • Failure scenarios (missing/incorrect CNAME, wrong A record, wrong CAA)
  • Proper status transitions (CREATED → CERTIFICATE_GENERATING)
  • Log message validation

The use of the static domain webapp.com explains the need for the tearDown cleanup.


707-738: Timestamp update validation is well-designed.

This test correctly verifies that re-running verification with unchanged DNS data updates the $updatedAt timestamp while preserving the original logs, ensuring proper idempotency behavior.

src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php (6)

31-34: Constructor properly delegates to parent.

The variadic parameter pattern allows flexible dependency injection and consistent initialization through the parent Action class.


69-69: Log injection properly configured.

The Log dependency is correctly injected and utilized in the verification flow at line 109, supporting the PR objective to enhance logging.

Also applies to: 73-73


75-75: Domain validation centralized effectively.

Calling validateDomainRestrictions($domain, $platform) centralizes validation logic in the Action parent class, reducing code duplication across rule handlers.


81-91: Status initialization with proper empty string guards.

The empty string checks at lines 86-87 correctly prevent str_ends_with($domain, '') from returning true for any domain, addressing the previous review concern. Appwrite-managed domains are appropriately marked as VERIFIED.


107-114: Verification logic correctly integrated into creation flow.

The verification executes before document creation (line 117), so both the status change to RULE_STATUS_CERTIFICATE_GENERATING and any error logs are properly persisted. The previous review concern about persistence was based on a misunderstanding—this is the CREATE path, not an UPDATE, so the subsequent createDocument call persists all changes.


122-129: Certificate queueing correctly gated by verification status.

Triggering certificate generation only when status === RULE_STATUS_CERTIFICATE_GENERATING ensures certificates are not attempted for unverified domains.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
tests/e2e/Services/Proxy/ProxyCustomServerTest.php (2)

573-705: Comprehensive verification test with DNS infrastructure dependency.

This test thoroughly validates 9 domain verification scenarios covering A, CNAME, and CAA record configurations. The hardcoded domains (e.g., 'stage-site.webapp.com', 'wrong-a-webapp.com') require corresponding DNS records in the test environment, which aligns with the CoreDNS test infrastructure mentioned in the PR summary.

The test structure is clear with numbered scenario comments and consistent create-assert-cleanup patterns.

Consider splitting this into separate test methods (e.g., testRuleVerificationWithCorrectARecord, testRuleVerificationWithWrongCNAME) for better isolation and maintainability. However, keeping them together in an E2E test reduces setup/teardown overhead, which is also reasonable.


718-718: Fix variable naming for consistency.

The variable $initiallogs should be $initialLogs to maintain camelCase consistency with other variables like $initialUpdatedAt (line 717).

Apply this diff:

-        $initiallogs = $rule['body']['logs'];
+        $initialLogs = $rule['body']['logs'];

And update references on lines 725 and 730:

-        $this->assertStringContainsString($initiallogs, $updatedRule['body']['message']);
+        $this->assertStringContainsString($initialLogs, $updatedRule['body']['message']);
-        $this->assertEquals($initiallogs, $ruleAfterUpdate['body']['logs']);
+        $this->assertEquals($initialLogs, $ruleAfterUpdate['body']['logs']);
src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php (1)

97-99: Consider using strict comparison for clarity.

While != works correctly here, using !== would be more idiomatic and explicit about type-safe comparison.

Apply this diff for stricter comparison:

     if (
-        ($functionsDomain != '' && \str_ends_with($domain, $functionsDomain)) ||
-        ($sitesDomain != '' && \str_ends_with($domain, $sitesDomain))
+        ($functionsDomain !== '' && \str_ends_with($domain, $functionsDomain)) ||
+        ($sitesDomain !== '' && \str_ends_with($domain, $sitesDomain))
     ) {
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 49b1c3d and 990209b.

📒 Files selected for processing (2)
  • src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php (6 hunks)
  • tests/e2e/Services/Proxy/ProxyCustomServerTest.php (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php (1)
src/Appwrite/Platform/Modules/Proxy/Http/Rules/Action.php (3)
  • Action (17-188)
  • validateDomainRestrictions (31-80)
  • verifyRule (89-187)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Setup & Build Appwrite Image
  • GitHub Check: Setup & Build Appwrite Image
  • GitHub Check: scan
🔇 Additional comments (8)
tests/e2e/Services/Proxy/ProxyCustomServerTest.php (2)

18-46: LGTM! Previous serialization issue has been addressed.

The tearDown method correctly cleans up test rules. Line 39 now properly serializes the query with ->toString(), addressing the previous review comment.


707-738: Test logic correctly validates timestamp updates.

The test properly verifies that re-running verification with the same data updates the $updatedAt timestamp while preserving the existing logs. The sleep(1) on line 720 is necessary to ensure a measurable timestamp difference in E2E testing.

src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php (6)

33-36: LGTM! Clean constructor pattern.

The variadic parameter forwarding to the parent constructor is a clean pattern that enables flexible initialization.


78-78: LGTM! Log injection aligns with new verification flow.

The addition of the Log parameter supports the new verification workflow and logging requirements.


80-80: LGTM! Centralized domain validation improves maintainability.

Delegating domain restriction validation to the Action base class reduces code duplication and centralizes the validation logic.


125-132: LGTM! Verification flow with appropriate error handling.

The new verification workflow correctly:

  • Attempts DNS validation for custom domains (status = CREATED)
  • Transitions to CERTIFICATE_GENERATING on success
  • Logs errors and maintains CREATED status on failure

97-103: Consistency verified across all Rule Create handlers.

The owner and status determination logic is identical across all create rules files (API, Function, Redirect, Site). No inconsistencies were found.


101-101: Certificate generation logic for VERIFIED domains is intentional and working as designed.

The current implementation correctly distinguishes between Appwrite-managed domains and custom domains:

  • Appwrite-managed domains (ending with $functionsDomain or $sitesDomain) are set to RULE_STATUS_VERIFIED at creation and skip both verification and certificate generation queuing in the Create handler
  • Custom domains follow the flow: RULE_STATUS_CREATED → verification → RULE_STATUS_CERTIFICATE_GENERATING → certificate generation queue

This pattern is consistent across all Create handlers (Function, Site, Redirect, API). The Verification/Update handler reinforces this design by returning early when status is already RULE_STATUS_VERIFIED (line 85), preventing redundant verification attempts.

Appwrite-managed domains do not require individual certificate generation because they are pre-configured with wildcard or shared certificates.

@hmacr hmacr requested a review from Meldiron December 16, 2025 10:56
@hmacr hmacr merged commit 58be8f4 into 1.8.x Dec 16, 2025
48 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants