# How FlClash's Profile Auto-Update System Handles Subscription Imports

> Learn how FlClash's auto-update system imports subscriptions. It refreshes profiles, fetches remote configurations, validates them with Clash core, and saves updated metadata.

- Repository: [chen08209/FlClash](https://github.com/chen08209/FlClash)
- Tags: internals
- Published: 2026-05-31

---

**FlClash automatically refreshes subscription profiles by comparing stored timestamps against configurable intervals, fetching remote configuration files, validating them through the Clash core, and persisting updated metadata including bandwidth usage statistics parsed from HTTP headers.**

FlClash implements a robust interval-driven mechanism to keep subscription profiles synchronized with remote sources. The `chen08209/FlClash` repository orchestrates this behavior through Dart model classes and provider actions that manage periodic updates while ensuring configuration integrity via the underlying Clash engine.

## Profile Structure and Auto-Update Configuration

### URL-Based Profile Attributes

In `lib/models/profile.dart`, the `Profile` class defines the data structure for subscription imports. Profiles imported from remote sources use `ProfileType.url` and store several critical fields that govern the auto-update behavior:

- **`url`** — The remote subscription address to fetch.
- **`autoUpdate`** — Boolean flag defaulting to **true** that controls whether the profile should refresh automatically.
- **`autoUpdateDuration`** — The `Duration` interval (defaulting to `defaultUpdateDuration`) specifying how long to wait between updates.
- **`lastUpdateDate`** — Timestamp recording the last successful import.
- **`subscriptionInfo`** — Parsed data from the `subscription-userinfo` HTTP header containing upload, download, total bandwidth, and expiration date.

These fields persist in the local database defined in `lib/database/profiles.dart`, which stores `auto_update`, `auto_update_duration_millis`, and `last_update_date` columns.

## The Auto-Update Orchestration Logic

### Entry Point: autoUpdateProfiles()

The central coordination happens in `lib/providers/action.dart` within the `ProfilesAction` class. The `autoUpdateProfiles()` method (around line 60) serves as the entry point invoked during app startup or periodic background tasks:

The method iterates through every stored profile and applies three filtering criteria to determine eligibility:

1. **Skip manual-update profiles** — Checks `if (!profile.autoUpdate) continue;` to respect user preferences.
2. **Exclude file-based profiles** — Skips local configurations with `if (profile.type == ProfileType.file) continue;` since only remote URLs require refreshing.
3. **Time-based validation** — Determines if an update is required by evaluating whether the stored `lastUpdateDate` plus the configured `autoUpdateDuration` falls before the current time:

```dart
final isNotNeedUpdate = profile.lastUpdateDate
    ?.add(profile.autoUpdateDuration)
    .isBeforeNow;
if (isNotNeedUpdate == false) continue;

```

### Individual Profile Updates

When a profile passes the time check, the system invokes `updateProfile()` (around line 90 in `lib/providers/action.dart`). This method wraps the core update logic and handles UI state notifications while delegating the actual import work to the `Profile` model.

## Subscription Import and Validation Process

### HTTP Fetch and Header Parsing

The `update()` method in `lib/models/profile.dart` (around line 200) handles the actual subscription import workflow:

First, it fetches the remote content using `request.getFileResponseForUrl(url)`. During this phase, the system extracts metadata from HTTP response headers:

- **`content-disposition`** — Used to generate default profile labels from the filename.
- **`subscription-userinfo`** — Parsed via `SubscriptionInfo.formHString` to extract upload bytes, download bytes, total bandwidth quota, and expiration timestamps.

### Core Validation and Persistence

Before activating the new configuration, FlClash validates the downloaded file:

1. **Temporary storage** — The subscription content saves to a temporary path.
2. **Configuration validation** — The file passes to `coreController.validateConfig(path)`, which verifies the configuration against the Clash core rules. Any validation message aborts the import.
3. **Permanent storage** — Upon successful validation, the system copies the temporary file to the profile's permanent location using `await tempFile.copy(mFile.path)`.
4. **Metadata refresh** — The method returns an updated `Profile` instance with `lastUpdateDate` set to `DateTime.now()` and the refreshed `subscriptionInfo`.

If an error occurs during any step, the exception is caught and logged, but the auto-update loop continues processing remaining profiles rather than failing entirely.

## Working with the Auto-Update API

You can interact with the auto-update system programmatically using the following patterns:

Trigger a manual refresh for a single subscription profile:

```dart
await ref.read(profilesActionProvider.notifier).updateProfile(myProfile);

```

Enable or modify auto-update settings for a specific profile:

```dart
await ref.read(profilesProvider.notifier).put(
  myProfile.copyWith(
    autoUpdate: true, 
    autoUpdateDuration: Duration(hours: 12)
  ),
);

```

Execute the periodic auto-update check (typically called at app launch or via background tasks):

```dart
await ref.read(profilesActionProvider.notifier).autoUpdateProfiles();

```

## Summary

- FlClash distinguishes between `ProfileType.url` and `ProfileType.file`, applying auto-updates only to remote subscriptions.
- The `autoUpdateProfiles()` method in `lib/providers/action.dart` orchestrates refreshes by checking the `lastUpdateDate` against `autoUpdateDuration` for each profile.
- The `Profile.update()` method in `lib/models/profile.dart` handles HTTP fetching, `subscription-userinfo` header parsing, and Clash core validation before persisting files.
- Errors during individual profile updates are isolated, ensuring one failed subscription does not block updates for others.
- Database persistence in `lib/database/profiles.dart` stores auto-update preferences and timestamps for offline scheduling.

## Frequently Asked Questions

### How does FlClash determine when to update a subscription profile?

FlClash calculates the next update time by adding the profile's `autoUpdateDuration` to its `lastUpdateDate`. When the app runs `autoUpdateProfiles()` in `lib/providers/action.dart`, it skips profiles where this calculated time is still in the future, refreshing only those whose intervals have elapsed.

### What happens if a subscription URL returns invalid configuration data?

During the update process in `lib/models/profile.dart`, FlClash passes the downloaded file to `coreController.validateConfig(path)`. If the Clash core returns validation errors, the import aborts before replacing the existing profile, preserving the last known good configuration while logging the error for diagnostics.

### Where does FlClash store auto-update settings and timestamps?

The application persists these values in a local Drift database defined in `lib/database/profiles.dart`, specifically within fields mapping to `auto_update`, `auto_update_duration_millis`, and `last_update_date`. This allows the auto-update scheduler to function correctly even after app restarts.

### Can users disable auto-updates for specific subscriptions while keeping them for others?

Yes. Each `Profile` instance maintains its own `autoUpdate` boolean flag defaulting to true. Users can disable automatic refreshing for individual profiles by setting `autoUpdate: false` via the settings API, which `autoUpdateProfiles()` checks via `if (!profile.autoUpdate) continue;` before attempting any network requests.