# GenPAC: one gfwlist source feeding eleven proxy config formats

> A Python generator that fetches gfwlist, layers your own AdBlock Plus style rules on top, and emits the config file whatever proxy client you happen to run.

**JinnLynn/genpac** — PAC/Dnsmasq/Wingy file Generator,  working with gfwlist, support custom rules.

- Repository: https://github.com/JinnLynn/genpac
- Stars: 2,182 · Forks: 314
- Language: Python
- License: MIT
- Published: 2026-10-07 · Updated: 2026-10-07 · Language: en
- Canonical page: https://hysenlabs.com/projects/jinnlynn-genpac

## Eleven output formats from a single fetch

GenPAC is a generator that takes gfwlist and writes configuration files for a range of proxy clients. The README lists the formats as PAC, Dnsmasq, V2Ray, Shadowsocks, Quantumult X, Shadowrocket, Surge, Wingy, Potatso, plus IP country lists, List in gfwlist's own format, and Copy for raw address lists. A `cooked` branch in the repository holds generated examples of each.

The CLI design follows from that breadth. You pick one format with `--format`, and the README is explicit that only the parameters belonging to the selected format become available. So the same binary handles a Surge rule set and a V2Ray routing document, but it will not quietly apply Surge flags to V2Ray output. Each family carries its own switches: `--pac-precise` and `--pac-compress` for PAC, `--dnsmasq-ipset` and `--dnsmasq-nftset` for Dnsmasq, `--shadowrocket-policy` for Shadowrocket, `--surge-policy` for Surge.

That single-letter shorthand is worth reading closely, because it is where the real differences live. The Dnsmasq target is the most interesting of the set, since the README explains that pairing Dnsmasq with iptables and ipset, or with nftables and nftset, gives you domain-based transparent proxying. The IP set and nft set flags both accept multiple comma-separated entries, and the nft set form encodes address family and table explicitly, as in `4#inet#TABLE#GFWLIST`.

## Installation is four lines, and the server extra is separate

The README keeps installation to a single fenced block, with the stable release, the development tarball, and the optional server component as distinct moves:

```bash
# 安装或更新
pip install genpac
# 安装开发版本
pip install https://github.com/JinnLynn/genpac/archive/dev.tar.gz

# 安装服务器组件
pip install genpac[server]
pip uninstall genpac
```

Splitting the server into an extra matters because the dependency list is small and deliberate. `pyproject.toml` pins `requests[socks]` at 2.31.0, PyYAML at 6.0.1 and netaddr at 1.3.0, and adds `publicsuffixlist` unpinned. The optional `server` extra then adds Werkzeug, Flask, Flask-APScheduler and watchdog. That reads as a real design: the generator needs to fetch and parse, while the long-running service needs a scheduler and a filesystem watcher, and you should not pay for the second set when you only want the first.

The declared version in `pyproject.toml` is 3.0.1, the package requires Python 3.8 or newer, and the classifiers cover 3.8 through 3.12. Notably, the project publishes no GitHub releases at all, so version tracking lives on PyPI rather than in the repository's own tag history.

## Custom rules use AdBlock Plus syntax and outrank gfwlist

GenPAC's differentiator is that your own rules take precedence over the source list. You can pass them inline with `--user-rule`, read them from a file with `--user-rule-from`, and both flags may be repeated. The README shows the equivalent ways of expressing the same two rules:

```bash
--user-rule="@@sina.com" --user-rule="||youtube.com"
--user-rule="@@sina.com,||youtube.com"
```

The syntax is AdBlock Plus filtering rules, which is a good choice because it is a syntax people already know. The README summarises it in six points: wildcard support, where `*.example.com/*` can be shortened to `.example.com/`; regular expressions delimited by backslashes; exception rules introduced by `@@` that keep matching addresses off the proxy; leading and trailing `|` anchors; the `||` marker that matches an address regardless of scheme, so `||example.com` covers http, https and ftp; and `!` for comments.

Precedence runs user-rule first, then user-rule-from, then gfwlist. The README also adds an unusual warning: configure custom rules carefully and try to avoid conflicting with gfwlist, or accidentally adding addresses that did not need a proxy in the first place. That is advice about a failure mode specific to this tool, since your exceptions silently override a curated list rather than sitting beside it.

## The generated rules check domains only, never URL paths

This is the single most important behavioural note in the README and it is buried in a warning near the top. The generated rules do not match URL paths. They check the domain, including subdomains, and nothing more. The example given makes it concrete: for `|http://sub2.sub1.domain.com/path/to/file.ext`, what gets matched is `sub2.sub1.domain.com`.

This follows from the source format rather than being an implementation shortcut. gfwlist entries are domain rules, so translating them into every supported target inherits the limitation. For PAC output it is invisible, since PAC already evaluates hostnames. For clients with rule sets that support path matching, you are giving up a capability the client offers, and the `--pac-precise` flag exists for tightening PAC evaluation specifically.

The practical consequence is that GenPAC cannot express "proxy this domain only on these paths", and custom rules cannot add that either, since they use the same domain-level matching. If your workflow depends on path-level granularity, you need to handle that in the client rather than in the generated file.

## Running it as a scheduled web service

GenPAC ships a server mode under the same package, started by a separate entry point and pointed at a config file:

```bash
genpac.server --config="/PATH/TO/CONFIG/FILE"
```

The Dockerfile shows how the container is assembled. It builds from a Python 3.12 base, copies the source and `pyproject.toml` into a staging directory, bakes in `example/server/config.ini` at `/app/etc/config.ini`, installs uwsgi along with the server extra, removes the build toolchain afterwards, and points `GENPAC_CONFIG` at that config path. The image runs through a wrapper script rather than uwsgi directly, and the entrypoint is what lets the same image work with or without a mounted config:

```bash
docker run --rm -p 8000:8000 jinnlynn/genpac
docker run --rm -p 8000:8000 -v /PATH/TO/CONFIG/FILE:/app/etc/config.ini jinnlynn/genpac
```

The scheduling libraries in the server extra, Flask-APScheduler and watchdog, indicate the intended pattern: regenerate on a timer and pick up edits to config or rule files without a restart. Since the package has no GitHub releases, the container tag behaviour is the other thing to check before depending on it in a pipeline.

## Two formats are already flagged as deprecated

The README marks two of its own output targets as deprecated. Wingy, described as an iOS proxy app built on NEKit, carries a note that it will soon be removed, and the same note appears under Potatso, Potatso2, another iOS app on the same underlying library. Both still have their flags documented, including Wingy's adapter options in a compound syntax where options within an adapter are comma-separated and adapters are separated by semicolons.

This is more informative than a stale entry would be. Two iOS clients on a shared NEKit base disappearing in the same direction tells you something about where the long tail of this ecosystem was heading, and the project noticed it early enough to say so in the README rather than leaving broken output formats in place. The repository is not archived, its last push was on 2026-09-28, and it carries an MIT license, so this is a maintained project whose target list is being pruned on purpose.

The repository tree reinforces that it is a well-formed Python package rather than a script: `src/` for the code, `tests/` and `tox.ini` for the test suite, `pyproject.toml` for modern packaging, and an `example/` directory holding a config file, a server directory, a user rules file and a Wingy template. The `tests/` directory and `tox.ini` pairing is the detail worth noting, since most projects of this size and vintage predate that convention.

## Conclusion

GenPAC's real contribution is not any single output format, since each one is a thin translation. It is the custom rule layer and the fact that one source list drives every target, so adding a domain means editing one file rather than hunting down the configuration dialect of whichever client you happen to be running. Two caveats deserve weight before you commit to it. The generated rules match domains and subdomains only, never URL paths, so anything that depended on path-level filtering will behave differently. And the Wingy and Potatso outputs are both marked deprecated in the README, which is a reasonable signal about how fast this project is tracking client churn. Start with a single `--format` run against your own client and inspect the output before wiring it into a scheduled refresh.

## FAQ

### What does GenPAC generate?

It fetches gfwlist and converts it into configuration files for proxy clients: PAC, Dnsmasq, V2Ray, Shadowsocks, Quantumult X, Shadowrocket, Surge, Wingy and Potatso, plus country IP lists, a raw gfwlist-format list, and a copy target for address lists. You select one format with `--format`, and only that format's own parameters become available.

### Do the generated rules match URL paths?

No. The README warns that generated rules never match the path, only the domain including subdomains. For `|http://sub2.sub1.domain.com/path/to/file.ext`, what is matched is `sub2.sub1.domain.com`. This comes from the gfwlist source format, and custom rules inherit it, so path-level filtering has to be handled in the client instead.

### How do custom rules take priority over gfwlist?

You add them with `--user-rule` or read them from a file with `--user-rule-from`, and both may be repeated. Precedence runs user-rule first, then user-rule-from, then gfwlist. The syntax is AdBlock Plus filtering rules, so `@@` marks an exception that stays off the proxy, `||` matches regardless of scheme, and `|` anchors the start or end of an address.

### Why did the gfwlist fetch fail, and how is it fixed?

gfwlist is fetched over the network, so the request can be blocked or otherwise fail. The README lists remedies including passing a proxy with `--proxy`, supplying a local copy with `--gfwlist-local`, and passing `-` as the URL to skip the online fetch entirely. The `--gfwlist-update-local` flag refreshes the local file when an online fetch succeeds.

## Sources

- [Issues](https://github.com/JinnLynn/genpac/issues)
- [JinnLynn/genpac on GitHub](https://github.com/JinnLynn/genpac)
- [License: MIT](https://github.com/JinnLynn/genpac/blob/master/LICENSE)
- [README](https://github.com/JinnLynn/genpac/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/jinnlynn-genpac
