Sync Rules resource (/admin/sync-rules/{id})¶
The rule page. It is the one place a SyncRule is acted on: its delivery is stopped or resumed, a hold is released or discarded, and the rule is edited, deleted or restored. It has no list and no navigation entry. A rule is reached from a connection's flow overview, from the Delivery page, or by its address.
Access¶
The page follows SyncRulePolicy::view(), inherited from AbstractResourcePolicy: sync_rules.view and a rule the user may access (SyncRule::accessibleBy(), which goes through the source connection's tenant). A rule outside that scope is not found. A soft-deleted rule stays reachable, so it can be restored.
Rule status¶
Every rule shows exactly one status, here and in the flow overview. App\Services\SyncRuleStatusResolver works it out; the first line that matches wins.
| # | Status | When |
|---|---|---|
| 0 | Deleted | The rule is soft-deleted. |
| 1 | Stopped, Throttled, Stale, Withdrawals held, Failing | DeliveryHealth reports that state (see Rule state). |
| 2 | Config problem | The direction matrix no longer allows the flow, or the target's writer cannot carry out the rule's policy. |
| 3 | Inactive | The rule is switched off. |
| 4 | Pull out | The target does not declare push_out for the domain; it fetches the data itself. |
| 5 | Route closed | Active and push_out, but the route is not open: the source or the target is not active, or the source no longer hands the domain over. |
| 6 | No run yet | The route is open and delivery has never run under the rule. |
| 7 | OK | DeliveryHealth reports ok. |
The delivery states come first so that a rule the Delivery page lists as held reads held everywhere, even after someone switched it off: the hold still stands and still needs a decision.
Sections¶
Header. The title is "Source → Target · Domain", each connection by its name, or by its system and host where it has none, as in "WooCommerce (shop-mueller.de) → Mueller CRM · Customers" (see how a connection is named), and the subheading gives the identity instead of the status: the source connection's tenant name. Header actions: Stop delivery (a reason is required; only for a push_out rule without a rule stop, permission delivery.stop), Abort backfill and Start backfill (see Backfill actions), Edit (update mode, delete policy, active; sync_rules.update), and a menu with Delete, Restore and Force delete (sync_rules.delete). Force delete stays disabled while the rule holds delivery state, its cursor or ledger rows; its runs and stops, resumed ones included, are deleted with it. Resuming a rule stop, and releasing or discarding a volume breaker hold, are not header actions: all three sit in the status slot below.
Status slot. Always present, coloured by the status, holding a badge, one bold sentence, detail and the actions that status calls for:
| Status | Sentence and detail | Actions |
|---|---|---|
| OK | Delivering normally; the last run's time and its delivered and suppressed counts, with ", N waiting for their parent" appended where the last run left records waiting | None |
| No run yet | Waiting for the first run; the next dispatch time from dispatch_cron |
None |
| Stale | Since when no run completed | Open the Delivery page |
| Throttled | Until when the target asked Conflux to wait, and that it applies to the whole connection | None |
| Withdrawals held | Pending withdrawals, what the target is known to hold, the threshold, when the hold was set, and a release in force if there is one | Release withdrawals (type the pending number), Discard hold, Show held records; the first two need delivery.release_withdrawals |
| Failing | What the last run attempted | Show failed records |
| Stopped | The stop's scope, who set it, when, and why | A rule stop offers Resume delivery; a connection stop links to the target connection; a global stop links to the Delivery page; a stop on the customers rule a consent rule rests on offers Open the customers rule instead (see below) |
| Config problem | Which check fails | Edit the rule, or the target system's declaration |
| Inactive | That nothing is delivered until it is activated | Edit the rule |
| Route closed | Which side is not available | A link to that connection |
| Pull out | That the target fetches this data itself and Conflux keeps no delivery state for the rule | None |
| Deleted | When the rule was deleted | Restore |
Resume delivery, Release withdrawals and Discard hold are each offered by their own condition rather than only by the headline status: any non-deleted rule with a live rule-level stop offers Resume, and any non-deleted rule with a live volume breaker hold offers Release and Discard, even where the headline shown ranks above withdrawals_held (Stale or Throttled). Restore is offered for any deleted rule, here and in the header menu.
A stopped rule's slot splits the stop into its parts: a line naming the stop's scope, who set it and when ("Rule stop set by Admin on 2026-09-25 21:11:01 UTC"), then the reason they gave on its own, set apart as a quote. A stopped or throttled rule whose backfill is still running adds "The backfill resumes where it stopped.", so nobody reads the wait as the backfill starting over. Under the sentence, a non-deleted rule with a policy divergence nobody has kept (see "Declared policy divergence" below) adds one more line, with a warning icon, naming the field and pointing at "Deletions" or "Updates" in the flow below.
A consent rule rests on its customers rule. A consent rule (a dependent domain) is anchored to the customers rule between the same two connections, and what stops or closes that rule applies to the consent rule too. The status slot says so and leads there:
- A stop that lives on the customers rule reads "Stopped through the customers rule: ..." and shows no Resume delivery, because the stop is not the consent rule's own. Open the customers rule links the customers rule's page, where the stop is lifted. A stop set on the consent rule itself still offers Resume.
- A closed route whose reason starts "The customers rule" or "Through the customers rule" gets the same link, and Open the customers rule replaces the link to the closed side.
- Where no customers rule exists, the link reads Open the source connection; the body says to create a customers rule from it, and the Edit action is hidden.
- The link is left out for a viewer who may not open the customers rule.
IDs, a collapsed section right after the status slot: the tenant's, the rule's, the source connection's and the target connection's UUIDs, each shown as a monospace chip with its last 8 characters. Clicking one copies it and a "Copied" tooltip confirms, like Filament's own copyable entries; the full UUID sits behind it as its tooltip.
Flow. Three columns, source, rule and target, with an arrow from each into the next on screens wide enough to set them side by side; there is no separate Configuration section. The source and the target each show the connection's name, linked to its page, and its host. Under Conflux reads, the source shows what Conflux reads from it for this domain, each as a label over its value: "Last bulk sync" for a pull_in domain and "Receives webhooks" for a push_in one (the webhook time is the latest on the whole connection, since a WebhookCall does not record its domain). The middle column, "This rule", is set apart as the bridge between the two: Status (Active or Inactive) and Updates side by side, Deletions under them. For a consent rule the Updates and Deletions entries give way to one sentence, "Consent is always overwritten in the target and never deleted.", since the rule's policy is fixed and the policy divergence checks do not apply; an Anchor line follows, reading "Anchor:
A check shows only where it fails, at what it concerns:
| Check | Where it shows |
|---|---|
| Direction matrix no longer allows the flow | As the flow's first line, red |
| The target's writer cannot carry out the rule's policy | At the foot of the "This rule" column, red, naming what the target cannot do (SyncRule::policyUndeliverableBecause()) |
| The update mode loses consent | Under Updates |
| A declared policy divergence (see below) | Under the diverging value, which is framed as a warning together with its actions |
Declared policy divergence. Where a rule's stored update_mode or delete_policy differs from what the target currently declares for the domain, a line under that value in the flow names the consequence. Only a field the target declares itself is compared: where it declares nothing for a field, there is no divergence, whatever the rule holds, since Conflux's own defaults are not a declaration. Both Switch to X (sets the field to the declared value through the normal save) and Keep Y (records the divergence as intended against the declaration in force now and the rule's current value) require the rule update permission (sync_rules.update, checked by SyncRulePolicy::update()) and refuse an unauthorized call, not only hiding the button. Switch to X is offered only where the declared value is deliverable; Keep Y is offered regardless, including for a declared value Conflux cannot yet deliver. A kept divergence shows who kept it and when instead of either action, and a later change of the declaration, or an edit of the rule to another value, brings the warning back. Force-deleting a rule deletes its kept decisions first: they reference the rule without a cascade.
Records. The rule's outbound_deliveries rows: counters for every status, and a table of Record, Status, Attempts, Next attempt, Last error and Delivered at, 25 rows per page. It opens on failed, exhausted and undeliverable; a counter narrows it to its status, and so does Show failed records or Show held records in the status slot. A record links to its Customer, Order or Product page where the mirror row still exists. A consent record links to its customer through its own customer_id; failing that, through a customer with source_id equal to its parent_source_id on the rule's own connection; failing that, through the ledger of the reverse customers rule (target to source), whose row for that remote customer names the shop customer. A record whose contact Conflux no longer knows shows no link. The status awaiting_anchor reads Waiting for parent. The table is read-only: when a record is tried again is the runner's decision.
Runs. The rule's delivery_runs, newest first, 20 rows per page, with the counters described under Runs, including Waiting: the records of that run that waited for their parent. Duration is written in words ("5 minutes"), not as a start and end time.
Every status keeps the same four blocks: status slot, IDs, flow, and the Records and Runs tabs. A Pull out rule keeps its tabs too; each says why instead of showing a table, since the target fetches the data itself and Conflux keeps no delivery state for the rule.
Backfill actions¶
Both actions sit in the page header and are gated by SyncRulePolicy::backfill(): the permission delivery.backfill (SuperAdmin and PlatformSupport) and a rule the user may manage, as for Stop delivery. Without both, neither button shows, and a forged call is refused. There is no console command for either action.
| Action | Shown when | What it does |
|---|---|---|
| Abort backfill | The rule is not deleted and a backfill is in progress, the initial one included | Asks for confirmation, then ends the backfill. Delta keeps running, the records the backfill had not reached are delivered once they change, and a new backfill can be started later. Where the backfill ended while the dialog was open, confirming does nothing and the dialog stays open; behind it, the header shows Start backfill instead. The abort is logged with the user and the rule, and not recorded in a table. |
| Start backfill | The rule is not deleted, is active, pushes out, belongs to a root domain (never a consent rule), has a cursor and has no backfill in progress. A stopped rule may start one, and it begins once the rule runs again | Opens a dialog with the number of records in the backfill's window and the number of creates the backfill would send. Both are counted once, when the dialog opens, against that moment as the bound, and stay as they are while the dialog is open. The window holds the records that last changed before the bound, and on a rule that propagates deletions, the deletions the source reported, which the backfill walks. The user ticks that the target was checked and types the number of creates. A number other than the one shown keeps the dialog open. On submit the service counts again against the same bound. Where the backfill would now send more creates, the dialog stays open and shows the new number to type; fewer creates start the backfill. Where a backfill started while the dialog was open, confirming does nothing and the dialog stays open; behind it, the header shows Abort backfill instead. A start writes a backfill confirmation. |
Which records count as a create is listed under backfill confirmations.
How to restart a backfill walks through both, and ADR-0046 gives the reason for the confirmation.
Source files¶
| File | Purpose |
|---|---|
app/Filament/Resources/SyncRules/SyncRuleResource.php |
Resource, scoped query, route binding that keeps deleted rules reachable |
app/Filament/Resources/SyncRules/Pages/ViewSyncRule.php |
Title, subheading, breadcrumbs, header actions |
app/Filament/Resources/SyncRules/Schemas/SyncRuleView.php |
Status badge, IDs, flow, checks, embedded tables |
app/Filament/Resources/SyncRules/Schemas/StatusSlot.php |
The status slot's headline, body and actions per status |
app/Filament/Resources/SyncRules/Actions/* |
Stop, resume, release, discard, start and abort a backfill, edit, restore, switch and keep policy |
app/Services/Backfills.php, app/Models/BackfillConfirmation.php |
Preview, start and abort of a backfill, and the confirmation a start records |
app/Services/PolicyDivergences.php, app/Data/PolicyDivergence.php |
Where a rule's stored policy differs from the target's declaration, and the consequence sentence |
app/Filament/Resources/SyncRules/Livewire/RuleDeliveryRecords.php |
Records table and counters |
app/Filament/Resources/SyncRules/Livewire/RuleDeliveryRuns.php |
Runs table |
app/Services/SyncRuleStatusResolver.php, app/Enums/SyncRuleStatus.php |
The status and its precedence |
app/Services/InboundActivity.php |
Last bulk sync and latest webhook of a connection and domain |