{"id":49606543,"url":"https://github.com/bavix/outway","last_synced_at":"2026-05-04T13:10:12.826Z","repository":{"id":316535273,"uuid":"1062752159","full_name":"bavix/outway","owner":"bavix","description":"Outway is a small service that steers egress traffic by domain. Policy is decided at the application layer (L7) using domain names from DNS, while enforcement is done at the network layer (L3/L4) by marking IPs and letting the OS route them (multi‑WAN by domains).","archived":false,"fork":false,"pushed_at":"2025-12-17T10:24:46.000Z","size":3045,"stargazers_count":2,"open_issues_count":21,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-03-29T14:51:33.900Z","etag":null,"topics":["dnsproxy"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bavix.png","metadata":{"files":{"readme":"README.md","changelog":"changelog.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-23T17:17:32.000Z","updated_at":"2025-11-16T20:58:51.000Z","dependencies_parsed_at":"2025-09-26T01:16:49.894Z","dependency_job_id":null,"html_url":"https://github.com/bavix/outway","commit_stats":null,"previous_names":["bavix/outway"],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/bavix/outway","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bavix%2Foutway","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bavix%2Foutway/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bavix%2Foutway/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bavix%2Foutway/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bavix","download_url":"https://codeload.github.com/bavix/outway/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bavix%2Foutway/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32608447,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-04T10:08:07.713Z","status":"ssl_error","status_checked_at":"2026-05-04T10:08:02.005Z","response_time":58,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dnsproxy"],"created_at":"2026-05-04T13:10:12.043Z","updated_at":"2026-05-04T13:10:12.819Z","avatar_url":"https://github.com/bavix.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Outway\n\nOutway is a small service that steers egress traffic by domain. Policy is decided at the application layer (L7) using domain names from DNS, while enforcement is done at the network layer (L3/L4) by marking IPs and letting the OS route them (multi‑WAN by domains).\n\nExample: you have two uplinks (wan1, wan2). You want `foo.example.com` via `wan1`, while `bar.example.com` always goes via `wan2`. With Outway you configure domain rules and the system enforces them.\n\n## How it works\n\n- Outway handles DNS queries for your apps\n- On each DNS answer, it extracts IPs, assigns a mark with TTL, and programs the OS firewall\n- Marked IPs follow the route/interface mapped to the matching domain rule group\n- When TTL expires, the mark is removed automatically\n\n## Features\n\n- Domain‑based egress routing (interface per rule group)\n- Request coalescing: deduplicates concurrent cache misses for the same host/QTYPE\n- Clean URL format for upstream resolvers (single, strict format):\n  - `udp://host[:port]` (default 53)\n  - `tcp://host[:port]` (default 53)\n  - `tls://host[:port]` or `dot://host[:port]` (default 853)\n  - `quic://host[:port]` or `doq://host[:port]` (default 853)\n  - `https://host/path` (DoH, RFC 8484)\n- Built‑in Admin UI with WebSocket realtime updates and polling fallback\n- Prometheus metrics at `/metrics`\n- Health endpoint at `/health`\n\n## Caching\n\n- LRU cache keyed by `fqdn:qtype` with per‑record TTL respected\n- Expired entries are evicted on read; fresh responses are cached\n- Singleflight coalescing prevents upstream stampedes for identical in‑flight queries\n\n## Quick start\n\n1) Install\n\n```bash\ngo install github.com/bavix/outway@latest\n```\n\n2) Configure\n\nUse `config.test.yaml` as a reference. Minimal realistic example (aligned with the test config):\n\n```yaml\napp_name: outway\n\nlisten:\n  udp: \":53\"\n  tcp: \":53\"\n\nupstreams:\n  - name: cloudflare-doh\n    address: https://cloudflare-dns.com/dns-query\n    weight: 1\n  - name: cf-ipv4\n    address: udp://1.1.1.1:53\n    weight: 1\n\nrule_groups:\n  - name: Default\n    description: Default egress group\n    via: utun4            # interface name\n    pin_ttl: true         # keep TTL from DNS, don't shrink aggressively\n    patterns:\n      - \"*.example.com\"\n```\n\nMulti‑WAN example:\n\n```yaml\nrule_groups:\n  - name: wan1-sites\n    via: wan1\n    pin_ttl: true\n    patterns:\n      - \"foo.example.com\"\n\n  - name: wan2-sites\n    via: wan2\n    pin_ttl: true\n    patterns:\n      - \"bar.example.com\"\n```\n\nFuller example (same structure as `config.test.yaml`):\n\n```yaml\napp_name: outway\n\nlisten:\n  udp: \":53\"\n  tcp: \":53\"\n\nupstreams:\n  - name: cloudflare-doh\n    address: https://cloudflare-dns.com/dns-query\n    weight: 1\n  - name: cf-ipv4\n    address: udp://1.1.1.1:53\n    weight: 1\n  - name: cf-ipv6\n    address: udp://[2606:4700:4700::1111]:53\n    weight: 1\n  - name: google\n    address: udp://8.8.8.8:53\n    weight: 1\n  - name: opendns\n    address: udp://208.67.222.222:53\n    weight: 1\n\nrule_groups:\n  - name: \"YouTube \u0026 Google Services\"\n    description: Route YouTube and Google services through specific interface\n    via: utun4\n    patterns:\n      - \"*.youtube.com\"\n      - \"*.googlevideo.com\"\n      - \"*.ytimg.com\"\n      - \"*.googleapis.com\"\n      - \"*.googleusercontent.com\"\n    pin_ttl: true\n\n  - name: Social Media\n    description: Social media platforms routing\n    via: utun4\n    patterns:\n      - \"*.instagram.com\"\n      - \"*.facebook.com\"\n      - \"*.twitter.com\"\n      - \"*.x.com\"\n      - \"*.tiktok.com\"\n      - \"*.snapchat.com\"\n    pin_ttl: true\n\n  - name: Streaming Services\n    description: Video streaming platforms\n    via: utun4\n    patterns:\n      - \"*.netflix.com\"\n      - \"*.hulu.com\"\n      - \"*.disney.com\"\n      - \"*.amazon.com\"\n      - \"*.twitch.tv\"\n    pin_ttl: true\n\n  - name: Development Tools\n    description: Development and version control services\n    via: utun3\n    patterns:\n      - \"*.github.com\"\n      - \"*.gitlab.com\"\n      - \"*.bitbucket.org\"\n      - \"*.docker.com\"\n      - \"*.npmjs.org\"\n    pin_ttl: true\n\n  - name: Blocked Domains\n    description: Blocked malicious domains\n    via: lo0\n    patterns:\n      - \"*.malware.com\"\n      - \"*.phishing-site.com\"\n      - \"*.ads-tracker.com\"\n    pin_ttl: true\n\nhistory:\n  enabled: true\n  max_entries: 10000\n\nlog:\n  level: info\n\ncache:\n  enabled: true\n  max_entries: 20000\n\nhttp:\n  enabled: true\n  listen: 127.0.0.1:47823\n  read_timeout: 30s\n  write_timeout: 30s\n  idle_timeout: 2m0s\n  max_header_bytes: 1048576\n\nhosts:\n  - pattern: localhost\n    a:\n      - 127.0.0.1\n    ttl: 60\n  - pattern: \"*.example.com\"\n    a:\n      - 127.0.0.1\n    ttl: 60\n```\n\n3) Run\n\n```bash\noutway run --config ./config.yaml\n```\n\n- Admin UI: `http://127.0.0.1:47823/`\n- Metrics: `http://127.0.0.1:47823/metrics`\n- Health: `http://127.0.0.1:47823/health`\n\n## Commands\n\n- `outway run` - Start the DNS proxy service\n- `outway cleanup` - Cleanup all firewall rules created by Outway\n- `outway self-update` - Update to the latest version from GitHub\n- `outway --version` - Show version information\n\n### Self-update\n\nUpdate Outway to the latest version:\n\n```bash\n# Update to latest stable version\noutway self-update\n\n# Include prerelease versions\noutway self-update --prerelease\n```\n\nThe self-update command will download the appropriate binary for your platform, replace the current binary, and exit with code 42 to trigger automatic restart by your init system (systemd, OpenWrt /etc/init.d, etc.).\n\n## Admin UI \u0026 configuration\n\n- UI: `http://127.0.0.1:47823/` (by default)\n- WebSocket realtime is used automatically; if WS is unavailable, UI switches to polling\n\nTo run the admin UI on a different address/port, configure the `http` section in the config:\n\n```yaml\nhttp:\n  enabled: true\n  listen: 0.0.0.0:8080   # address:port for Admin UI and API\n  read_timeout: 30s\n  write_timeout: 30s\n  idle_timeout: 2m0s\n```\n\nUpstreams in YAML are specified only in URL format — the type is derived from the scheme (`udp://`, `tcp://`, `dot://`, `doq://`, `https://`).\n\n## Observability\n\n- `/metrics` exposes Prometheus metrics (query rate, latency, marks, etc.)\n- UI Dashboard shows:\n  - Uptime\n  - Queries in the last minute and error count (realtime)\n  - Cache hit rate (when available)\n\n## System backends\n\n- Linux: nftables/iptables\n- macOS: pf\n\n## Build\n\n```bash\nmake build\nmake lint\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbavix%2Foutway","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbavix%2Foutway","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbavix%2Foutway/lists"}