How Rule-Based Routing and Rules Providers Are Managed in Clash Nyanpasu
Clash Nyanpasu delegates rule-based routing to the upstream Clash core while exposing a React-based UI layer that lets users view, refresh, and edit rule providers through a Tauri IPC bridge.
Clash Nyanpasu is a modern GUI application that wraps the Clash core, providing users with intuitive control over complex proxy configurations. Understanding how rule-based routing and rules providers are managed within this architecture requires examining the clear separation between the Rust-based backend, the IPC communication layer, and the React frontend. This implementation allows users to manage external rule sets without manually editing YAML configuration files.
Architecture Overview
The system follows a three-tier architecture that cleanly separates concerns between traffic routing logic and user interface management.
The Three Layers
-
Clash Core (Backend): Written in Rust, this handles the actual
rulesarray evaluation andrule-providersparsing. It exposes HTTP endpoints at/providers/rulesfor querying and updating providers. -
IPC Service (Middle Layer): The Tauri-based backend in
backend/tauri/src/server/mod.rsregisters route handlers that bridge the Clash core's API to the frontend. -
React UI (Frontend): Located in
frontend/nyanpasu/src/components/providers/, this layer uses React Query to cache provider state and renders interactive components for manual refreshes.
Backend Implementation
The Tauri server acts as a thin proxy between the Clash core and the frontend application.
Route Registration
In backend/tauri/src/server/mod.rs, the router exposes two critical endpoints for rule provider management:
router.route(
"/providers/rules",
get(providers_rules_handler) // GET → returns JSON of all rule providers
.put(put_providers_rules_handler) // PUT /:name → refresh a specific provider
);
The providers_rules_handler retrieves the current state of all rule providers from the Clash core, while put_providers_rules_handler triggers a download-and-reparse cycle for remote rule sets. When a refresh is initiated, the backend downloads the remote rule file, updates the provider metadata including ruleCount and updatedAt, and persists the changes to the appropriate .dat files on disk. The configuration parsing logic that handles the rule-providers section from the Clash YAML profile is located in backend/tauri/src/core/config/profile.rs, which validates and updates provider entries when remote sources are refreshed.
Frontend Data Flow
The frontend uses a custom React Query hook to manage server state and provide mutation capabilities for each provider.
The useClashRulesProvider Hook
Located in frontend/interface/src/ipc/use-clash-rules-provider.ts, this hook encapsulates the IPC communication:
export const useClashRulesProvider = () => {
const { providersRules, putProvidersRules } = useClashAPI()
const query = useQuery({
queryKey: [CLASH_RULES_PROVIDER_QUERY_KEY],
queryFn: async () => {
const { providers } = await providersRules()
return Object.fromEntries(
Object.entries(providers).map(([key, value]) => [
key,
{ ...value, mutate: async () => {
await putProvidersRules(key) // backend PUT /providers/rules/:key
await query.refetch()
}},
]),
) as ClashRulesProviderQuery
},
})
return query
}
This implementation caches provider data using the CLASH_RULES_PROVIDER_QUERY_KEY identifier. Each provider object returned by the hook includes a mutate function that executes putProvidersRules(key), sending a PUT request to /providers/rules/:name and automatically refetching the query to update the UI with fresh metadata.
UI Components
The visual interface for managing providers is built around the RulesProvider component.
The RulesProvider Component
Found in frontend/nyanpasu/src/components/providers/rules-provider.tsx, this React component renders individual provider cards:
export default function RulesProvider({ provider }: RulesProviderProps) {
const { t } = useTranslation()
const [loading, setLoading] = useState(false)
const handleClick = useLockFn(async () => {
try {
setLoading(true)
await provider.mutate() // triggers backend refresh
} finally {
setLoading(false)
}
})
return (
<Paper className="flex flex-col gap-2 p-5">
<div className="flex items-start justify-between">
<p className="text-lg font-bold">{provider.name}</p>
<div>{t('Last Update', { fromNow: dayjs(provider.updatedAt).fromNow() })}</div>
</div>
<Chip label={t('Rule Set rules', { rule: provider.ruleCount })} />
<Button loading={loading} onClick={handleClick}>
<Refresh />
</Button>
</Paper>
)
}
The component displays the provider's name, vehicleType, behavior, and ruleCount. It uses dayjs to render human-readable timestamps via dayjs(provider.updatedAt).fromNow(). The refresh button utilizes useLockFn to prevent duplicate requests during the asynchronous update operation. This component is consumed by both the Legacy Providers page at frontend/nyanpasu/src/pages/(legacy)/providers.tsx and the Update Providers component at frontend/nyanpasu/src/components/providers/update-providers.tsx.
Rule-Based Routing Mechanics
While the UI manages provider metadata, the actual traffic routing logic remains inside the Clash core.
Configuration Structure
The Clash configuration file defines routing through two key sections:
rulesarray: Evaluated in order, mapping traffic to proxy groups orRULE-SETreferencesrule-providersmap: Defines external rule sets (GeoIP, GeoSite) that the core loads dynamically
When the core processes a connection, it iterates through the rules array. Entries referencing RULE-SET types point to providers defined in rule-providers, which can specify remote URLs for automatic updates. The core automatically reloads updated .dat files on the next routing decision after a successful refresh from the UI.
Summary
- Separation of concerns: The Clash core handles traffic matching while Clash Nyanpasu provides a management interface.
- Backend routes:
backend/tauri/src/server/mod.rsregisters/providers/rulesendpoints for fetching and updating providers. - Frontend state:
useClashRulesProviderinfrontend/interface/src/ipc/use-clash-rules-provider.tsuses React Query to cache provider data and expose mutation functions. - UI rendering: The
RulesProvidercomponent infrontend/nyanpasu/src/components/providers/rules-provider.tsxdisplays metadata and triggers refreshes viaprovider.mutate(). - Data flow: User clicks trigger
putProvidersRules()→ backend downloads remote rule sets → core reloads automatically → UI refetches viaquery.refetch().
Frequently Asked Questions
How does Clash Nyanpasu update rule providers?
When a user clicks the refresh button in the RulesProvider component, the mutate function calls putProvidersRules(key), which sends a PUT request to /providers/rules/:name on the Tauri backend. The backend then downloads the remote rule file, updates the provider's ruleCount and updatedAt metadata, and writes the new rules to disk for the Clash core to load.
What is the difference between rule-based routing and rule providers?
Rule-based routing refers to the Clash core's process of evaluating the rules array against each connection to determine the appropriate proxy. Rule providers are external data sources (like GeoIP or GeoSite databases) defined in rule-providers that supply large rule sets to the RULE-SET entries in the main rules array, allowing dynamic updates without editing the main configuration file.
Where does the actual traffic matching logic execute?
The traffic matching logic executes entirely within the upstream Clash core (Rust), not in the Clash Nyanpasu GUI. Clash Nyanpasu only manages the metadata and refresh cycles for rule providers through the IPC layer, while the core handles real-time packet evaluation against the loaded rule sets.
How does the UI reflect when a provider was last updated?
The UI reads the updatedAt timestamp from each provider object and renders it using dayjs(provider.updatedAt).fromNow() in the RulesProvider component. This displays a human-readable relative time (such as "3 hours ago") next to the provider name, and the ruleCount chip shows the current number of rules loaded from that provider.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →