[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: mordavid\npatreon: mordavid\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\nlfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry\npolar: # Replace with a single Polar username\nbuy_me_a_coffee: mordavid\nthanks_dev: # Replace with a single thanks.dev username\ncustom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": "FlareTunnel.go",
    "content": "package main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"crypto/x509/pkix\"\n\t\"encoding/json\"\n\t\"encoding/pem\"\n\t\"fmt\"\n\t\"io\"\n\t\"math/big\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/olekukonko/tablewriter\"\n)\n\n// ====================================================================\n// CONSTANTS & TYPES\n// ====================================================================\n\nconst (\n\tVersion           = \"1.0.0\"\n\tCloudflareBaseURL = \"https://api.cloudflare.com/client/v4\"\n\tWorkerScript      = `/**\n * FlareTunnel - Cloudflare Worker URL Redirection Script\n */\naddEventListener('fetch', event => {\n  event.respondWith(handleRequest(event.request))\n})\n\nasync function handleRequest(request) {\n  try {\n    const url = new URL(request.url)\n    const targetUrl = getTargetUrl(url, request.headers)\n\n    if (!targetUrl) {\n      return createErrorResponse('No target URL specified', {\n        usage: {\n          query_param: '?url=https://example.com',\n          header: 'X-Target-URL: https://example.com',\n          path: '/https://example.com'\n        }\n      }, 400)\n    }\n\n    let targetURL\n    try {\n      targetURL = new URL(targetUrl)\n    } catch (e) {\n      return createErrorResponse('Invalid target URL', { provided: targetUrl }, 400)\n    }\n\n    // Build target URL with filtered query parameters\n    const targetParams = new URLSearchParams()\n    for (const [key, value] of url.searchParams) {\n      if (!['url', '_cb', '_t'].includes(key)) {\n        targetParams.append(key, value)\n      }\n    }\n    if (targetParams.toString()) {\n      targetURL.search = targetParams.toString()\n    }\n\n    // Create proxied request\n    const proxyRequest = createProxyRequest(request, targetURL)\n    const response = await fetch(proxyRequest)\n\n    // Process and return response\n    return createProxyResponse(response, request.method)\n\n  } catch (error) {\n    return createErrorResponse('Proxy request failed', {\n      message: error.message,\n      timestamp: new Date().toISOString()\n    }, 500)\n  }\n}\n\nfunction getTargetUrl(url, headers) {\n  // Priority: query param > header > path\n  let targetUrl = url.searchParams.get('url')\n\n  if (!targetUrl) {\n    targetUrl = headers.get('X-Target-URL')\n  }\n\n  if (!targetUrl && url.pathname !== '/') {\n    const pathUrl = url.pathname.slice(1)\n    if (pathUrl.startsWith('http')) {\n      targetUrl = pathUrl\n    }\n  }\n\n  return targetUrl\n}\n\nfunction createProxyRequest(request, targetURL) {\n  const proxyHeaders = new Headers()\n  const allowedHeaders = [\n    'accept', 'accept-language', 'accept-encoding', 'authorization',\n    'cache-control', 'content-type', 'origin', 'referer', 'user-agent'\n  ]\n\n  // Copy allowed headers\n  for (const [key, value] of request.headers) {\n    if (allowedHeaders.includes(key.toLowerCase())) {\n      proxyHeaders.set(key, value)\n    }\n  }\n\n  proxyHeaders.set('Host', targetURL.hostname)\n\n  // Set X-Forwarded-For header\n  const customXForwardedFor = request.headers.get('X-My-X-Forwarded-For')\n  if (customXForwardedFor) {\n    proxyHeaders.set('X-Forwarded-For', customXForwardedFor)\n  } else {\n    proxyHeaders.set('X-Forwarded-For', generateRandomIP())\n  }\n\n  return new Request(targetURL.toString(), {\n    method: request.method,\n    headers: proxyHeaders,\n    body: ['GET', 'HEAD'].includes(request.method) ? null : request.body\n  })\n}\n\nfunction createProxyResponse(response, requestMethod) {\n  const responseHeaders = new Headers()\n\n  // Copy ALL response headers (let browser handle encoding)\n  for (const [key, value] of response.headers) {\n    responseHeaders.set(key, value)\n  }\n\n  // Add/Override CORS headers\n  responseHeaders.set('Access-Control-Allow-Origin', '*')\n  responseHeaders.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS, PATCH, HEAD')\n  responseHeaders.set('Access-Control-Allow-Headers', '*')\n\n  if (requestMethod === 'OPTIONS') {\n    return new Response(null, { status: 204, headers: responseHeaders })\n  }\n\n  return new Response(response.body, {\n    status: response.status,\n    statusText: response.statusText,\n    headers: responseHeaders\n  })\n}\n\nfunction createErrorResponse(error, details, status) {\n  return new Response(JSON.stringify({ error, ...details }), {\n    status,\n    headers: { 'Content-Type': 'application/json' }\n  })\n}\n\nfunction generateRandomIP() {\n  return [1, 2, 3, 4].map(() => Math.floor(Math.random() * 255) + 1).join('.')\n}`\n)\n\n// Account represents a Cloudflare account configuration\ntype Account struct {\n\tName      string `json:\"name\"`\n\tAPIToken  string `json:\"api_token\"`\n\tAccountID string `json:\"account_id\"`\n\tZoneID    string `json:\"zone_id,omitempty\"`\n}\n\n// Config represents the FlareTunnel configuration\ntype Config struct {\n\tAccounts []Account `json:\"accounts\"`\n}\n\n// Worker represents a Cloudflare Worker deployment\ntype Worker struct {\n\tName              string `json:\"name\"`\n\tURL               string `json:\"url\"`\n\tCreatedAt         string `json:\"created_at\"`\n\tID                string `json:\"id\"`\n\tAccountID         string `json:\"account_id\"`\n\tConfigAccountName string `json:\"config_account_name,omitempty\"`\n}\n\n// Analytics represents worker analytics data\ntype Analytics struct {\n\tSuccess       bool           `json:\"success\"`\n\tTotalRequests int            `json:\"total_requests\"`\n\tPerWorker     map[string]int `json:\"per_worker\"`\n\tLimit         int            `json:\"limit\"`\n\tError         string         `json:\"error,omitempty\"`\n}\n\n// ====================================================================\n// CLOUDFLARE API CLIENT\n// ====================================================================\n\ntype CloudflareClient struct {\n\tAPIToken  string\n\tAccountID string\n\tBaseURL   string\n\tHeaders   map[string]string\n\tsubdomain string\n}\n\nfunc NewCloudflareClient(apiToken, accountID string) *CloudflareClient {\n\treturn &CloudflareClient{\n\t\tAPIToken:  apiToken,\n\t\tAccountID: accountID,\n\t\tBaseURL:   CloudflareBaseURL,\n\t\tHeaders: map[string]string{\n\t\t\t\"Authorization\": \"Bearer \" + apiToken,\n\t\t\t\"Content-Type\":  \"application/json\",\n\t\t},\n\t}\n}\n\nfunc (c *CloudflareClient) GetSubdomain() (string, error) {\n\tif c.subdomain != \"\" {\n\t\treturn c.subdomain, nil\n\t}\n\n\turl := fmt.Sprintf(\"%s/accounts/%s/workers/subdomain\", c.BaseURL, c.AccountID)\n\treq, err := http.NewRequest(\"GET\", url, nil)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tfor k, v := range c.Headers {\n\t\treq.Header.Set(k, v)\n\t}\n\n\tclient := &http.Client{Timeout: 30 * time.Second}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\tc.subdomain = strings.ToLower(c.AccountID)\n\t\treturn c.subdomain, nil\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode == 200 {\n\t\tvar result struct {\n\t\t\tResult struct {\n\t\t\t\tSubdomain string `json:\"subdomain\"`\n\t\t\t} `json:\"result\"`\n\t\t}\n\t\tif err := json.NewDecoder(resp.Body).Decode(&result); err == nil {\n\t\t\tif result.Result.Subdomain != \"\" {\n\t\t\t\tc.subdomain = result.Result.Subdomain\n\t\t\t\treturn c.subdomain, nil\n\t\t\t}\n\t\t}\n\t}\n\n\tc.subdomain = strings.ToLower(c.AccountID)\n\treturn c.subdomain, nil\n}\n\nfunc (c *CloudflareClient) CreateWorker(name string) (*Worker, error) {\n\tif name == \"\" {\n\t\tname = generateWorkerName()\n\t}\n\n\turl := fmt.Sprintf(\"%s/accounts/%s/workers/scripts/%s\", c.BaseURL, c.AccountID, name)\n\n\tvar b bytes.Buffer\n\twriter := multipartWriter(&b)\n\t\n\tmetadata := map[string]string{\n\t\t\"body_part\":   \"script\",\n\t\t\"main_module\": \"worker.js\",\n\t}\n\tmetadataJSON, _ := json.Marshal(metadata)\n\t\n\twriter.WriteField(\"metadata\", string(metadataJSON))\n\twriter.WriteField(\"script\", WorkerScript)\n\tcontentType := writer.FormDataContentType()\n\twriter.Close()\n\n\treq, err := http.NewRequest(\"PUT\", url, &b)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treq.Header.Set(\"Authorization\", \"Bearer \"+c.APIToken)\n\treq.Header.Set(\"Content-Type\", contentType)\n\n\tclient := &http.Client{Timeout: 60 * time.Second}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != 200 {\n\t\tbody, _ := io.ReadAll(resp.Body)\n\t\treturn nil, fmt.Errorf(\"failed to create worker: %s\", string(body))\n\t}\n\n\t// Enable subdomain\n\tsubdomain, _ := c.GetSubdomain()\n\tsubdomainURL := fmt.Sprintf(\"%s/accounts/%s/workers/scripts/%s/subdomain\", c.BaseURL, c.AccountID, name)\n\tsubdomainBody := bytes.NewBufferString(`{\"enabled\":true}`)\n\tsubdomainReq, _ := http.NewRequest(\"POST\", subdomainURL, subdomainBody)\n\tfor k, v := range c.Headers {\n\t\tsubdomainReq.Header.Set(k, v)\n\t}\n\tclient.Do(subdomainReq)\n\n\tworkerURL := fmt.Sprintf(\"https://%s.%s.workers.dev\", name, subdomain)\n\n\treturn &Worker{\n\t\tName:      name,\n\t\tURL:       workerURL,\n\t\tCreatedAt: time.Now().Format(\"2006-01-02 15:04:05\"),\n\t\tID:        name,\n\t\tAccountID: c.AccountID,\n\t}, nil\n}\n\nfunc (c *CloudflareClient) ListWorkers() ([]*Worker, error) {\n\turl := fmt.Sprintf(\"%s/accounts/%s/workers/scripts\", c.BaseURL, c.AccountID)\n\treq, err := http.NewRequest(\"GET\", url, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor k, v := range c.Headers {\n\t\treq.Header.Set(k, v)\n\t}\n\n\tclient := &http.Client{Timeout: 30 * time.Second}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer resp.Body.Close()\n\n\tvar result struct {\n\t\tResult []struct {\n\t\t\tID        string `json:\"id\"`\n\t\t\tCreatedOn string `json:\"created_on\"`\n\t\t} `json:\"result\"`\n\t}\n\n\tif err := json.NewDecoder(resp.Body).Decode(&result); err != nil {\n\t\treturn nil, err\n\t}\n\n\tsubdomain, _ := c.GetSubdomain()\n\tworkers := []*Worker{}\n\n\tfor _, script := range result.Result {\n\t\tif strings.HasPrefix(script.ID, \"flaretunnel-\") {\n\t\t\tworkers = append(workers, &Worker{\n\t\t\t\tName:      script.ID,\n\t\t\t\tURL:       fmt.Sprintf(\"https://%s.%s.workers.dev\", script.ID, subdomain),\n\t\t\t\tCreatedAt: script.CreatedOn,\n\t\t\t\tAccountID: c.AccountID,\n\t\t\t})\n\t\t}\n\t}\n\n\treturn workers, nil\n}\n\nfunc (c *CloudflareClient) DeleteWorker(name string) error {\n\turl := fmt.Sprintf(\"%s/accounts/%s/workers/scripts/%s\", c.BaseURL, c.AccountID, name)\n\treq, err := http.NewRequest(\"DELETE\", url, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor k, v := range c.Headers {\n\t\treq.Header.Set(k, v)\n\t}\n\n\tclient := &http.Client{Timeout: 30 * time.Second}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != 200 && resp.StatusCode != 404 {\n\t\treturn fmt.Errorf(\"failed to delete worker: status %d\", resp.StatusCode)\n\t}\n\n\treturn nil\n}\n\nfunc (c *CloudflareClient) GetAnalytics() (*Analytics, error) {\n\tgraphqlURL := \"https://api.cloudflare.com/client/v4/graphql\"\n\n\tnow := time.Now()\n\tdateStart := now.Add(-24 * time.Hour).Format(\"2006-01-02T15:04:05Z\")\n\tdateEnd := now.Format(\"2006-01-02T15:04:05Z\")\n\n\tquery := `\n\t\tquery WorkersAnalytics($accountTag: string!, $datetimeStart: string!, $datetimeEnd: string!) {\n\t\t\tviewer {\n\t\t\t\taccounts(filter: {accountTag: $accountTag}) {\n\t\t\t\t\tworkersInvocationsAdaptive(\n\t\t\t\t\t\tlimit: 10000\n\t\t\t\t\t\tfilter: {\n\t\t\t\t\t\t\tdatetime_geq: $datetimeStart\n\t\t\t\t\t\t\tdatetime_leq: $datetimeEnd\n\t\t\t\t\t\t}\n\t\t\t\t\t\torderBy: [sum_requests_DESC]\n\t\t\t\t\t) {\n\t\t\t\t\t\tdimensions {\n\t\t\t\t\t\t\tscriptName\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsum {\n\t\t\t\t\t\t\trequests\n\t\t\t\t\t\t\terrors\n\t\t\t\t\t\t\tsubrequests\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t`\n\n\tvariables := map[string]interface{}{\n\t\t\"accountTag\":     c.AccountID,\n\t\t\"datetimeStart\":  dateStart,\n\t\t\"datetimeEnd\":    dateEnd,\n\t}\n\n\tpayload := map[string]interface{}{\n\t\t\"query\":     query,\n\t\t\"variables\": variables,\n\t}\n\n\tpayloadBytes, _ := json.Marshal(payload)\n\treq, err := http.NewRequest(\"POST\", graphqlURL, bytes.NewBuffer(payloadBytes))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor k, v := range c.Headers {\n\t\treq.Header.Set(k, v)\n\t}\n\n\tclient := &http.Client{Timeout: 30 * time.Second}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn &Analytics{Success: false, Limit: 100000, PerWorker: make(map[string]int)}, nil\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != 200 {\n\t\treturn &Analytics{Success: false, Limit: 100000, PerWorker: make(map[string]int)}, nil\n\t}\n\n\tvar result struct {\n\t\tData struct {\n\t\t\tViewer struct {\n\t\t\t\tAccounts []struct {\n\t\t\t\t\tWorkersInvocationsAdaptive []struct {\n\t\t\t\t\t\tDimensions struct {\n\t\t\t\t\t\t\tScriptName string `json:\"scriptName\"`\n\t\t\t\t\t\t} `json:\"dimensions\"`\n\t\t\t\t\t\tSum struct {\n\t\t\t\t\t\t\tRequests int `json:\"requests\"`\n\t\t\t\t\t\t} `json:\"sum\"`\n\t\t\t\t\t} `json:\"workersInvocationsAdaptive\"`\n\t\t\t\t} `json:\"accounts\"`\n\t\t\t} `json:\"viewer\"`\n\t\t} `json:\"data\"`\n\t}\n\n\tif err := json.NewDecoder(resp.Body).Decode(&result); err != nil {\n\t\treturn &Analytics{Success: false, Limit: 100000, PerWorker: make(map[string]int)}, nil\n\t}\n\n\tanalytics := &Analytics{\n\t\tSuccess:   true,\n\t\tLimit:     100000,\n\t\tPerWorker: make(map[string]int),\n\t}\n\n\tif len(result.Data.Viewer.Accounts) > 0 {\n\t\tfor _, inv := range result.Data.Viewer.Accounts[0].WorkersInvocationsAdaptive {\n\t\t\tscriptName := inv.Dimensions.ScriptName\n\t\t\trequests := inv.Sum.Requests\n\t\t\tanalytics.PerWorker[scriptName] = requests\n\t\t\tanalytics.TotalRequests += requests\n\t\t}\n\t}\n\n\treturn analytics, nil\n}\n\n// ====================================================================\n// SSL CERTIFICATE GENERATION\n// ====================================================================\n\nfunc generateCACert(certPath, keyPath string) error {\n\tif _, err := os.Stat(certPath); err == nil {\n\t\tif _, err := os.Stat(keyPath); err == nil {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\tpriv, err := rsa.GenerateKey(rand.Reader, 2048)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tnotBefore := time.Now()\n\tnotAfter := notBefore.Add(3650 * 24 * time.Hour)\n\n\tserialNumber, _ := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))\n\n\ttemplate := x509.Certificate{\n\t\tSerialNumber: serialNumber,\n\t\tSubject: pkix.Name{\n\t\t\tCountry:      []string{\"US\"},\n\t\t\tProvince:     []string{\"CA\"},\n\t\t\tLocality:     []string{\"Local\"},\n\t\t\tOrganization: []string{\"FlareTunnel\"},\n\t\t\tCommonName:   \"FlareTunnel CA\",\n\t\t},\n\t\tNotBefore:             notBefore,\n\t\tNotAfter:              notAfter,\n\t\tKeyUsage:              x509.KeyUsageCertSign | x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,\n\t\tExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},\n\t\tBasicConstraintsValid: true,\n\t\tIsCA:                  true,\n\t}\n\n\tderBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tcertOut, err := os.Create(certPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tpem.Encode(certOut, &pem.Block{Type: \"CERTIFICATE\", Bytes: derBytes})\n\tcertOut.Close()\n\n\tkeyOut, err := os.Create(keyPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tpem.Encode(keyOut, &pem.Block{Type: \"RSA PRIVATE KEY\", Bytes: x509.MarshalPKCS1PrivateKey(priv)})\n\tkeyOut.Close()\n\n\tfmt.Printf(\"✓ Generated CA certificate: %s\\n\", certPath)\n\treturn nil\n}\n\nfunc generateHostCert(hostname, caCertPath, caKeyPath string) (*tls.Certificate, error) {\n\tcaCertPEM, err := os.ReadFile(caCertPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tblock, _ := pem.Decode(caCertPEM)\n\tcaCert, err := x509.ParseCertificate(block.Bytes)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcaKeyPEM, err := os.ReadFile(caKeyPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tkeyBlock, _ := pem.Decode(caKeyPEM)\n\tcaKey, err := x509.ParsePKCS1PrivateKey(keyBlock.Bytes)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tpriv, err := rsa.GenerateKey(rand.Reader, 2048)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnotBefore := time.Now()\n\tnotAfter := notBefore.Add(365 * 24 * time.Hour)\n\n\tserialNumber, _ := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))\n\n\ttemplate := x509.Certificate{\n\t\tSerialNumber: serialNumber,\n\t\tSubject: pkix.Name{\n\t\t\tCountry:      []string{\"US\"},\n\t\t\tProvince:     []string{\"CA\"},\n\t\t\tLocality:     []string{\"Local\"},\n\t\t\tOrganization: []string{\"FlareTunnel\"},\n\t\t\tCommonName:   hostname,\n\t\t},\n\t\tNotBefore:             notBefore,\n\t\tNotAfter:              notAfter,\n\t\tKeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,\n\t\tExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},\n\t\tBasicConstraintsValid: true,\n\t\tDNSNames:              []string{hostname, \"*.\" + hostname},\n\t}\n\n\tderBytes, err := x509.CreateCertificate(rand.Reader, &template, caCert, &priv.PublicKey, caKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcertPEM := pem.EncodeToMemory(&pem.Block{Type: \"CERTIFICATE\", Bytes: derBytes})\n\tkeyPEM := pem.EncodeToMemory(&pem.Block{Type: \"RSA PRIVATE KEY\", Bytes: x509.MarshalPKCS1PrivateKey(priv)})\n\n\tcert, err := tls.X509KeyPair(certPEM, keyPEM)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &cert, nil\n}\n\n// ====================================================================\n// FLARETUNNEL MANAGER\n// ====================================================================\n\ntype FlareTunnel struct {\n\tConfig          *Config\n\tClients         map[string]*CloudflareClient\n\tEndpointsFile   string\n\tConfigFile      string\n\tworkers         []*Worker\n\tworkersMutex    sync.RWMutex\n}\n\nfunc NewFlareTunnel(configFile string) (*FlareTunnel, error) {\n\tif configFile == \"\" {\n\t\tconfigFile = \"flaretunnel.json\"\n\t}\n\n\tconfig, err := loadConfig(configFile)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tft := &FlareTunnel{\n\t\tConfig:        config,\n\t\tClients:       make(map[string]*CloudflareClient),\n\t\tEndpointsFile: \"flaretunnel_endpoints.json\",\n\t\tConfigFile:    configFile,\n\t}\n\n\tfor _, account := range config.Accounts {\n\t\tft.Clients[account.Name] = NewCloudflareClient(account.APIToken, account.AccountID)\n\t}\n\n\treturn ft, nil\n}\n\nfunc loadConfig(path string) (*Config, error) {\n\tdata, err := os.ReadFile(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Remove BOM if present (UTF-8-sig compatibility)\n\tif len(data) >= 3 && data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF {\n\t\tdata = data[3:]\n\t}\n\n\tvar config Config\n\tif err := json.Unmarshal(data, &config); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &config, nil\n}\n\nfunc (ft *FlareTunnel) SaveEndpoints(workers []*Worker) error {\n\tdata, err := json.MarshalIndent(workers, \"\", \"  \")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn os.WriteFile(ft.EndpointsFile, data, 0644)\n}\n\nfunc (ft *FlareTunnel) LoadEndpoints() ([]*Worker, error) {\n\tdata, err := os.ReadFile(ft.EndpointsFile)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Remove BOM if present\n\tif len(data) >= 3 && data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF {\n\t\tdata = data[3:]\n\t}\n\n\tvar workers []*Worker\n\tif err := json.Unmarshal(data, &workers); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn workers, nil\n}\n\nfunc (ft *FlareTunnel) SyncEndpoints() ([]*Worker, error) {\n\tallWorkers := []*Worker{}\n\n\tfor accountName, client := range ft.Clients {\n\t\tworkers, err := client.ListWorkers()\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor _, w := range workers {\n\t\t\tw.ConfigAccountName = accountName\n\t\t\tallWorkers = append(allWorkers, w)\n\t\t}\n\t}\n\n\tif len(allWorkers) > 0 {\n\t\tft.SaveEndpoints(allWorkers)\n\t}\n\n\treturn allWorkers, nil\n}\n\nfunc (ft *FlareTunnel) CreateWorkers(count int, accountName string, distribute bool) error {\n\tfmt.Printf(\"\\nCreating %d FlareTunnel endpoint(s)...\\n\", count)\n\n\tcreated := []*Worker{}\n\n\tif accountName != \"\" {\n\t\t// Single account\n\t\tclient, ok := ft.Clients[accountName]\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"account '%s' not found\", accountName)\n\t\t}\n\n\t\tfmt.Printf(\"   Using account: %s\\n\", accountName)\n\n\t\tfor i := 0; i < count; i++ {\n\t\t\tworker, err := client.CreateWorker(\"\")\n\t\t\tif err != nil {\n\t\t\t\tfmt.Printf(\"  [%d/%d] Failed: %v\\n\", i+1, count, err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tworker.ConfigAccountName = accountName\n\t\t\tcreated = append(created, worker)\n\t\t\tfmt.Printf(\"  [%d/%d] %s -> %s\\n\", i+1, count, worker.Name, worker.URL)\n\t\t}\n\t} else if distribute && len(ft.Clients) > 1 {\n\t\t// Distribute across accounts\n\t\tfmt.Printf(\"   Distribution mode: Checking quotas across %d account(s)...\\n\", len(ft.Clients))\n\n\t\taccountQuotas := make(map[string]int)\n\t\tfor name, client := range ft.Clients {\n\t\t\tanalytics, _ := client.GetAnalytics()\n\t\t\tremaining := 100000 - analytics.TotalRequests\n\t\t\taccountQuotas[name] = remaining\n\t\t\tfmt.Printf(\"      %s: %d requests remaining\\n\", name, remaining)\n\t\t}\n\n\t\ttotalQuota := 0\n\t\tfor _, quota := range accountQuotas {\n\t\t\ttotalQuota += quota\n\t\t}\n\n\t\tif totalQuota == 0 {\n\t\t\ttotalQuota = len(ft.Clients)\n\t\t\tfor name := range accountQuotas {\n\t\t\t\taccountQuotas[name] = 1\n\t\t\t}\n\t\t}\n\n\t\tworkersPerAccount := make(map[string]int)\n\t\tfor name, quota := range accountQuotas {\n\t\t\tproportion := float64(quota) / float64(totalQuota)\n\t\t\tworkersPerAccount[name] = max(1, int(float64(count)*proportion))\n\t\t}\n\n\t\t// Adjust to exact count\n\t\tfor sum := sumMap(workersPerAccount); sum < count; sum = sumMap(workersPerAccount) {\n\t\t\tmaxAccount := \"\"\n\t\t\tmaxQuota := 0\n\t\t\tfor name, quota := range accountQuotas {\n\t\t\t\tif quota > maxQuota {\n\t\t\t\t\tmaxQuota = quota\n\t\t\t\t\tmaxAccount = name\n\t\t\t\t}\n\t\t\t}\n\t\t\tworkersPerAccount[maxAccount]++\n\t\t}\n\n\t\tfmt.Printf(\"\\n   Distribution plan:\\n\")\n\t\tfor name, wc := range workersPerAccount {\n\t\t\tfmt.Printf(\"      %s: %d worker(s)\\n\", name, wc)\n\t\t}\n\t\tfmt.Println()\n\n\t\tcreatedCount := 0\n\t\tfor name, wc := range workersPerAccount {\n\t\t\tclient := ft.Clients[name]\n\t\t\tfor i := 0; i < wc; i++ {\n\t\t\t\tworker, err := client.CreateWorker(\"\")\n\t\t\t\tif err != nil {\n\t\t\t\t\tfmt.Printf(\"  [%d/%d] [%s] Failed: %v\\n\", createdCount+1, count, name, err)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tworker.ConfigAccountName = name\n\t\t\t\tcreated = append(created, worker)\n\t\t\t\tcreatedCount++\n\t\t\t\tfmt.Printf(\"  [%d/%d] [%s] %s -> %s\\n\", createdCount, count, name, worker.Name, worker.URL)\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// Default to first account\n\t\tvar firstClient *CloudflareClient\n\t\tvar firstName string\n\t\tfor name, client := range ft.Clients {\n\t\t\tfirstClient = client\n\t\t\tfirstName = name\n\t\t\tbreak\n\t\t}\n\n\t\tfmt.Printf(\"   Using account: %s\\n\", firstName)\n\n\t\tfor i := 0; i < count; i++ {\n\t\t\tworker, err := firstClient.CreateWorker(\"\")\n\t\t\tif err != nil {\n\t\t\t\tfmt.Printf(\"  [%d/%d] Failed: %v\\n\", i+1, count, err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tworker.ConfigAccountName = firstName\n\t\t\tcreated = append(created, worker)\n\t\t\tfmt.Printf(\"  [%d/%d] %s -> %s\\n\", i+1, count, worker.Name, worker.URL)\n\t\t}\n\t}\n\n\tft.SyncEndpoints()\n\tfmt.Printf(\"\\nCreated: %d, Failed: %d\\n\", len(created), count-len(created))\n\n\treturn nil\n}\n\nfunc (ft *FlareTunnel) ListWorkers(verbose, checkStatus bool) error {\n\tworkers, err := ft.SyncEndpoints()\n\tif err != nil || len(workers) == 0 {\n\t\tfmt.Println(\"❌ No FlareTunnel endpoints found\")\n\t\tfmt.Println(\"💡 Create some with: go run FlareTunnel.go create --count 5\")\n\t\treturn nil\n\t}\n\n\t// Verbose mode: check status as well\n\tif verbose {\n\t\tcheckStatus = true // Force status check in verbose mode!\n\t}\n\n\t// Get analytics for all accounts\n\tallAnalytics := make(map[string]*Analytics)\n\tfor accountName, client := range ft.Clients {\n\t\tanalytics, _ := client.GetAnalytics()\n\t\tallAnalytics[accountName] = analytics\n\t}\n\n\tif checkStatus {\n\t\tfmt.Printf(\"\\n🔍 Checking status of %d worker(s)...\\n\", len(workers))\n\t\tfmt.Println(\"This may take a few seconds...\\n\")\n\t}\n\n\tfmt.Println()\n\ttable := tablewriter.NewWriter(os.Stdout)\n\t\n\t// Build header based on flags\n\theader := []string{\"#\", \"Account\", \"Name\"}\n\tif verbose {\n\t\theader = append(header, \"Created\", \"Age\")\n\t}\n\theader = append(header, \"URL\", \"Requests\")\n\tif checkStatus {\n\t\theader = append(header, \"Status (ms)\")\n\t} else {\n\t\theader = append(header, \"Status\")\n\t}\n\ttable.SetHeader(header)\n\ttable.SetBorder(true)\n\n\tfor idx, worker := range workers {\n\t\trow := []string{strconv.Itoa(idx), worker.ConfigAccountName, worker.Name}\n\t\t\n\t\t// Add verbose info (Created, Age)\n\t\tif verbose {\n\t\t\tcreatedStr := \"Unknown\"\n\t\t\tageStr := \"Unknown\"\n\t\t\t\n\t\t\t// Extract timestamp from worker name: flaretunnel-1764721536-kmoiok\n\t\t\tre := regexp.MustCompile(`flaretunnel-(\\d+)-`)\n\t\t\tif matches := re.FindStringSubmatch(worker.Name); len(matches) > 1 {\n\t\t\t\tif timestamp, err := strconv.ParseInt(matches[1], 10, 64); err == nil {\n\t\t\t\t\tcreatedTime := time.Unix(timestamp, 0)\n\t\t\t\t\tcreatedStr = createdTime.Format(\"2006-01-02 15:04:05\")\n\t\t\t\t\t\n\t\t\t\t\t// Calculate age\n\t\t\t\t\tage := time.Since(createdTime)\n\t\t\t\t\tif age.Hours() < 1 {\n\t\t\t\t\t\tageStr = fmt.Sprintf(\"%dm ago\", int(age.Minutes()))\n\t\t\t\t\t} else if age.Hours() < 24 {\n\t\t\t\t\t\tageStr = fmt.Sprintf(\"%dh ago\", int(age.Hours()))\n\t\t\t\t\t} else {\n\t\t\t\t\t\tageStr = fmt.Sprintf(\"%dd ago\", int(age.Hours()/24))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\trow = append(row, createdStr, ageStr)\n\t\t}\n\t\t\n\t\t// Add URL\n\t\trow = append(row, worker.URL)\n\t\t\n\t\t// Add requests count\n\t\trequests := \"0\"\n\t\tif analytics, ok := allAnalytics[worker.ConfigAccountName]; ok && analytics.Success {\n\t\t\tif req, ok := analytics.PerWorker[worker.Name]; ok {\n\t\t\t\trequests = strconv.Itoa(req)\n\t\t\t}\n\t\t}\n\t\trow = append(row, requests)\n\t\t\n\t\t// Add status\n\t\tif checkStatus {\n\t\t\t// Test actual worker\n\t\t\ttestURL := worker.URL + \"?url=\" + url.QueryEscape(\"https://httpbin.org/status/200\")\n\t\t\tstart := time.Now()\n\t\t\tclient := &http.Client{Timeout: 5 * time.Second}\n\t\t\tresp, err := client.Get(testURL)\n\t\t\telapsed := time.Since(start)\n\t\t\t\n\t\t\tif err != nil {\n\t\t\t\trow = append(row, \"❌ Failed\")\n\t\t\t} else {\n\t\t\t\tresp.Body.Close()\n\t\t\t\tif resp.StatusCode == 200 {\n\t\t\t\t\trow = append(row, fmt.Sprintf(\"✅ %dms\", elapsed.Milliseconds()))\n\t\t\t\t} else {\n\t\t\t\t\trow = append(row, fmt.Sprintf(\"⚠️ %d\", resp.StatusCode))\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\trow = append(row, \"✅ Active\")\n\t\t}\n\t\t\n\t\ttable.Append(row)\n\t}\n\n\ttable.Render()\n\n\tfmt.Println(\"\\n💡 Use worker index with: go run FlareTunnel.go tunnel --workers 0,1,2 --verbose\")\n\tfmt.Println()\n\n\treturn nil\n}\n\nfunc (ft *FlareTunnel) CleanupWorkers(accountName string) error {\n\tif accountName != \"\" {\n\t\tclient, ok := ft.Clients[accountName]\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"account '%s' not found\", accountName)\n\t\t}\n\n\t\tfmt.Printf(\"\\n🗑️  Cleaning up account: %s\\n\", accountName)\n\t\tworkers, _ := client.ListWorkers()\n\n\t\tfor _, worker := range workers {\n\t\t\tif err := client.DeleteWorker(worker.Name); err != nil {\n\t\t\t\tfmt.Printf(\"   ✗ Failed to delete: %s\\n\", worker.Name)\n\t\t\t} else {\n\t\t\t\tfmt.Printf(\"   ✓ Deleted: %s\\n\", worker.Name)\n\t\t\t}\n\t\t}\n\t} else {\n\t\tfmt.Printf(\"\\n🗑️  Cleaning up ALL accounts (%d total)\\n\", len(ft.Clients))\n\n\t\tfor name, client := range ft.Clients {\n\t\t\tfmt.Printf(\"   Account: %s\\n\", name)\n\t\t\tworkers, _ := client.ListWorkers()\n\n\t\t\tfor _, worker := range workers {\n\t\t\t\tif err := client.DeleteWorker(worker.Name); err != nil {\n\t\t\t\t\tfmt.Printf(\"   ✗ Failed to delete: %s\\n\", worker.Name)\n\t\t\t\t} else {\n\t\t\t\t\tfmt.Printf(\"   ✓ Deleted: %s\\n\", worker.Name)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tos.Remove(ft.EndpointsFile)\n\t}\n\n\treturn nil\n}\n\nfunc (ft *FlareTunnel) TestWorkers(targetURL, method string) error {\n\tworkers, err := ft.LoadEndpoints()\n\tif err != nil || len(workers) == 0 {\n\t\tfmt.Println(\"❌ No workers available\")\n\t\treturn fmt.Errorf(\"no workers found\")\n\t}\n\n\tfmt.Printf(\"\\nTesting %d FlareTunnel endpoint(s) with %s\\n\", len(workers), targetURL)\n\n\tsuccessCount := 0\n\tuniqueIPs := make(map[string]bool)\n\n\tfor _, worker := range workers {\n\t\tfmt.Printf(\"\\nTesting endpoint: %s\\n\", worker.Name)\n\n\t\ttestURL := worker.URL + \"?url=\" + url.QueryEscape(targetURL)\n\t\t\n\t\tclient := &http.Client{Timeout: 30 * time.Second}\n\t\treq, err := http.NewRequest(method, testURL, nil)\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"   ✗ Request failed: %v\\n\", err)\n\t\t\tcontinue\n\t\t}\n\n\t\tresp, err := client.Do(req)\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"   ✗ Request failed: %v\\n\", err)\n\t\t\tcontinue\n\t\t}\n\t\tdefer resp.Body.Close()\n\n\t\tif resp.StatusCode == 200 {\n\t\t\tsuccessCount++\n\t\t\tfmt.Printf(\"   ✓ Request successful! Status: %d\\n\", resp.StatusCode)\n\n\t\t\tbody, _ := io.ReadAll(resp.Body)\n\t\t\tbodyStr := strings.TrimSpace(string(body))\n\n\t\t\t// Try to extract IP from common formats\n\t\t\tif strings.Contains(targetURL, \"ifconfig.me\") {\n\t\t\t\tfmt.Printf(\"   Origin IP: %s\\n\", bodyStr)\n\t\t\t\tuniqueIPs[bodyStr] = true\n\t\t\t} else if strings.Contains(targetURL, \"httpbin.org/ip\") {\n\t\t\t\tvar data map[string]interface{}\n\t\t\t\tif json.Unmarshal(body, &data) == nil {\n\t\t\t\t\tif origin, ok := data[\"origin\"].(string); ok {\n\t\t\t\t\t\tfmt.Printf(\"   Origin IP: %s\\n\", origin)\n\t\t\t\t\t\tuniqueIPs[origin] = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfmt.Printf(\"   Response Length: %d bytes\\n\", len(body))\n\t\t\t}\n\t\t} else {\n\t\t\tfmt.Printf(\"   ✗ Request failed! Status: %d\\n\", resp.StatusCode)\n\t\t}\n\t}\n\n\tfmt.Printf(\"\\nTest Results:\\n\")\n\tfmt.Printf(\"   Working endpoints: %d/%d\\n\", successCount, len(workers))\n\tif len(uniqueIPs) > 0 {\n\t\tfmt.Printf(\"   Unique IP addresses: %d\\n\", len(uniqueIPs))\n\t\tfor ip := range uniqueIPs {\n\t\t\tfmt.Printf(\"      - %s\\n\", ip)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (ft *FlareTunnel) ExportConfig(outputFile string) error {\n\tif _, err := os.Stat(\"flaretunnel.json\"); os.IsNotExist(err) {\n\t\tfmt.Println(\"❌ No configuration file found (flaretunnel.json)\")\n\t\treturn err\n\t}\n\n\tconfigData, err := os.ReadFile(\"flaretunnel.json\")\n\tif err != nil {\n\t\tfmt.Printf(\"❌ Failed to read configuration: %v\\n\", err)\n\t\treturn err\n\t}\n\n\texportData := map[string]interface{}{\n\t\t\"exported_at\": time.Now().Format(time.RFC3339),\n\t\t\"config\":      json.RawMessage(configData),\n\t}\n\n\tdata, err := json.MarshalIndent(exportData, \"\", \"  \")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := os.WriteFile(outputFile, data, 0644); err != nil {\n\t\tfmt.Printf(\"❌ Failed to write export file: %v\\n\", err)\n\t\treturn err\n\t}\n\n\tvar config Config\n\tjson.Unmarshal(configData, &config)\n\n\tfmt.Printf(\"✅ Exported configuration to %s\\n\", outputFile)\n\tfmt.Println(\"\\n📊 Summary:\")\n\tfmt.Printf(\"   Accounts: %d\\n\", len(config.Accounts))\n\tfor _, acc := range config.Accounts {\n\t\tfmt.Printf(\"      • %s: %s...\\n\", acc.Name, acc.AccountID[:min(8, len(acc.AccountID))])\n\t}\n\tfmt.Println(\"\\n⚠️  WARNING: This file contains API tokens! Keep it secure! 🔒\")\n\n\treturn nil\n}\n\nfunc (ft *FlareTunnel) ImportConfig(inputFile string, merge bool) error {\n\tif _, err := os.Stat(inputFile); os.IsNotExist(err) {\n\t\tfmt.Printf(\"❌ Import file not found: %s\\n\", inputFile)\n\t\treturn err\n\t}\n\n\timportData, err := os.ReadFile(inputFile)\n\tif err != nil {\n\t\tfmt.Printf(\"❌ Failed to read import file: %v\\n\", err)\n\t\treturn err\n\t}\n\n\t// Remove BOM if present\n\tif len(importData) >= 3 && importData[0] == 0xEF && importData[1] == 0xBB && importData[2] == 0xBF {\n\t\timportData = importData[3:]\n\t}\n\n\tvar importWrapper map[string]interface{}\n\tvar newConfig Config\n\n\tif err := json.Unmarshal(importData, &importWrapper); err != nil {\n\t\tfmt.Printf(\"❌ Failed to parse import file: %v\\n\", err)\n\t\treturn err\n\t}\n\n\t// Check if it's an export format or direct config\n\tif configData, ok := importWrapper[\"config\"]; ok {\n\t\tconfigBytes, _ := json.Marshal(configData)\n\t\tjson.Unmarshal(configBytes, &newConfig)\n\t} else {\n\t\tjson.Unmarshal(importData, &newConfig)\n\t}\n\n\tif len(newConfig.Accounts) == 0 {\n\t\tfmt.Println(\"❌ No accounts found in import file\")\n\t\treturn fmt.Errorf(\"no accounts in import\")\n\t}\n\n\tfmt.Printf(\"📥 Import file: %s\\n\", inputFile)\n\tfmt.Printf(\"   Accounts: %d\\n\\n\", len(newConfig.Accounts))\n\n\tfor idx, acc := range newConfig.Accounts {\n\t\tfmt.Printf(\"   %d. %s: %s...\\n\", idx+1, acc.Name, acc.AccountID[:min(8, len(acc.AccountID))])\n\t}\n\n\tvar finalConfig Config\n\n\tif merge && fileExists(\"flaretunnel.json\") {\n\t\texistingData, _ := os.ReadFile(\"flaretunnel.json\")\n\t\tjson.Unmarshal(existingData, &finalConfig)\n\n\t\texistingNames := make(map[string]bool)\n\t\tfor _, acc := range finalConfig.Accounts {\n\t\t\texistingNames[acc.Name] = true\n\t\t}\n\n\t\tfor _, acc := range newConfig.Accounts {\n\t\t\tif !existingNames[acc.Name] {\n\t\t\t\tfinalConfig.Accounts = append(finalConfig.Accounts, acc)\n\t\t\t\tfmt.Printf(\"➕ Added account: %s\\n\", acc.Name)\n\t\t\t} else {\n\t\t\t\tfmt.Printf(\"⚠️  Skipped duplicate: %s\\n\", acc.Name)\n\t\t\t}\n\t\t}\n\t} else {\n\t\tfinalConfig = newConfig\n\t}\n\n\treader := bufio.NewReader(os.Stdin)\n\tfmt.Print(\"\\nConfirm import? (y/N): \")\n\tconfirm, _ := reader.ReadString('\\n')\n\n\tif strings.ToLower(strings.TrimSpace(confirm)) != \"y\" {\n\t\tfmt.Println(\"Import cancelled.\")\n\t\treturn nil\n\t}\n\n\tdata, _ := json.MarshalIndent(finalConfig, \"\", \"  \")\n\tif err := os.WriteFile(\"flaretunnel.json\", data, 0644); err != nil {\n\t\tfmt.Printf(\"❌ Failed to save configuration: %v\\n\", err)\n\t\treturn err\n\t}\n\n\tfmt.Println(\"\\n✅ Configuration imported!\")\n\tfmt.Printf(\"📊 Total accounts: %d\\n\", len(finalConfig.Accounts))\n\n\treturn nil\n}\n\nfunc min(a, b int) int {\n\tif a < b {\n\t\treturn a\n\t}\n\treturn b\n}\n\nfunc fileExists(path string) bool {\n\t_, err := os.Stat(path)\n\treturn err == nil\n}\n\n// ====================================================================\n// PROXY SERVER\n// ====================================================================\n\ntype ProxyServer struct {\n\tHost                 string\n\tPort                 int\n\tWorkers              []*Worker\n\tCurrentWorkerIndex   int\n\tRotationMode         string\n\tVerbose              bool\n\tAllowIPAccess        bool\n\tCACertPath           string\n\tCAKeyPath            string\n\tBlacklistPatterns    []string\n\tInlineBlockPatterns  []string\n\tBlacklistStats       map[string]int\n\tUpstreamProxy        string\n\tUpstreamVerifySSL    bool\n\tCacheCerts           bool\n\tNoSSLIntercept       bool\n\tmutex                sync.Mutex\n\tcertCache            map[string]*tls.Certificate\n\tcertMutex            sync.RWMutex\n}\n\nfunc NewProxyServer(host string, port int) *ProxyServer {\n\treturn &ProxyServer{\n\t\tHost:              host,\n\t\tPort:              port,\n\t\tRotationMode:      \"round-robin\",\n\t\tBlacklistStats:    make(map[string]int),\n\t\tcertCache:         make(map[string]*tls.Certificate),\n\t}\n}\n\nfunc (ps *ProxyServer) LoadWorkers(endpointsFile string, workerIndices []int) error {\n\tdata, err := os.ReadFile(endpointsFile)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar allWorkers []*Worker\n\tif err := json.Unmarshal(data, &allWorkers); err != nil {\n\t\treturn err\n\t}\n\n\tif len(workerIndices) > 0 {\n\t\tselected := []*Worker{}\n\t\tfor _, idx := range workerIndices {\n\t\t\tif idx >= 0 && idx < len(allWorkers) {\n\t\t\t\tselected = append(selected, allWorkers[idx])\n\t\t\t}\n\t\t}\n\t\tps.Workers = selected\n\t} else {\n\t\tps.Workers = allWorkers\n\t}\n\n\treturn nil\n}\n\nfunc (ps *ProxyServer) LoadBlacklist(blacklistFile string) error {\n\tpatterns := []string{}\n\n\t// Load from file\n\tif blacklistFile != \"\" {\n\t\tif _, err := os.Stat(blacklistFile); err == nil {\n\t\t\tfile, err := os.Open(blacklistFile)\n\t\t\tif err == nil {\n\t\t\t\tdefer file.Close()\n\t\t\t\tscanner := bufio.NewScanner(file)\n\n\t\t\t\tfor scanner.Scan() {\n\t\t\t\t\tline := strings.TrimSpace(scanner.Text())\n\t\t\t\t\tif line != \"\" && !strings.HasPrefix(line, \"#\") {\n\t\t\t\t\t\tpatterns = append(patterns, line)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif len(patterns) > 0 {\n\t\t\t\t\tfmt.Printf(\"✅ Loaded %d patterns from %s\\n\", len(patterns), blacklistFile)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Add inline patterns\n\tif len(ps.InlineBlockPatterns) > 0 {\n\t\tpatterns = append(patterns, ps.InlineBlockPatterns...)\n\t\tfmt.Printf(\"✅ Added %d inline patterns\\n\", len(ps.InlineBlockPatterns))\n\t}\n\n\tps.BlacklistPatterns = patterns\n\n\treturn nil\n}\n\nfunc (ps *ProxyServer) GetWorkerURL() string {\n\tif len(ps.Workers) == 0 {\n\t\treturn \"\"\n\t}\n\n\tps.mutex.Lock()\n\tdefer ps.mutex.Unlock()\n\n\tif ps.RotationMode == \"random\" {\n\t\treturn ps.Workers[time.Now().UnixNano()%int64(len(ps.Workers))].URL\n\t} else if ps.RotationMode == \"round-robin\" {\n\t\tworker := ps.Workers[ps.CurrentWorkerIndex]\n\t\tps.CurrentWorkerIndex = (ps.CurrentWorkerIndex + 1) % len(ps.Workers)\n\t\treturn worker.URL\n\t}\n\n\treturn ps.Workers[0].URL\n}\n\nfunc (ps *ProxyServer) IsBlacklisted(targetURL string) bool {\n\tif len(ps.BlacklistPatterns) == 0 {\n\t\treturn false\n\t}\n\n\turlLower := strings.ToLower(targetURL)\n\tparsedURL, _ := url.Parse(targetURL)\n\thostname := \"\"\n\tpath := \"\"\n\n\tif parsedURL != nil {\n\t\thostname = strings.ToLower(parsedURL.Hostname())\n\t\tpath = strings.ToLower(parsedURL.Path)\n\t}\n\n\tfor _, pattern := range ps.BlacklistPatterns {\n\t\tpatternLower := strings.ToLower(pattern)\n\t\tif strings.Contains(hostname, patternLower) ||\n\t\t\tstrings.Contains(path, patternLower) ||\n\t\t\tstrings.Contains(urlLower, patternLower) ||\n\t\t\tstrings.HasSuffix(urlLower, patternLower) {\n\n\t\t\tps.mutex.Lock()\n\t\t\tps.BlacklistStats[pattern]++\n\t\t\tps.mutex.Unlock()\n\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc (ps *ProxyServer) HandleHTTP(w http.ResponseWriter, r *http.Request) {\n\ttargetURL := r.URL.String()\n\tif !strings.HasPrefix(targetURL, \"http://\") && !strings.HasPrefix(targetURL, \"https://\") {\n\t\ttargetURL = \"http://\" + r.Host + r.URL.String()\n\t}\n\n\tif ps.IsBlacklisted(targetURL) {\n\t\tif ps.Verbose {\n\t\t\tfmt.Printf(\"🚫 BLOCKED (blacklist): %s\\n\", targetURL)\n\t\t}\n\t\thttp.Error(w, \"Blacklisted\", http.StatusForbidden)\n\t\treturn\n\t}\n\n\tparsedURL, _ := url.Parse(targetURL)\n\tif !ps.AllowIPAccess && isIPAddress(parsedURL.Hostname()) {\n\t\tif ps.Verbose {\n\t\t\tfmt.Printf(\"🛡️  BLOCKED (IP): %s\\n\", parsedURL.Hostname())\n\t\t}\n\t\thttp.Error(w, \"Direct IP access blocked\", http.StatusForbidden)\n\t\treturn\n\t}\n\n\tworkerURL := ps.GetWorkerURL()\n\tif workerURL == \"\" {\n\t\thttp.Error(w, \"No workers available\", http.StatusServiceUnavailable)\n\t\treturn\n\t}\n\n\tproxyURL := workerURL + \"?url=\" + url.QueryEscape(targetURL)\n\n\tif ps.Verbose {\n\t\tfmt.Printf(\"\\n📤 [%s] %s\\n\", r.Method, targetURL)\n\t\tfmt.Printf(\"   ↓ via Worker: %s\\n\", workerURL)\n\t}\n\n\t// Create proxy request\n\tproxyReq, err := http.NewRequest(r.Method, proxyURL, r.Body)\n\tif err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\t// Copy headers\n\tfor k, v := range r.Header {\n\t\tif !strings.EqualFold(k, \"Host\") && !strings.EqualFold(k, \"Connection\") {\n\t\t\tproxyReq.Header[k] = v\n\t\t}\n\t}\n\n\t// Send request with optional upstream proxy\n\ttransport := &http.Transport{\n\t\tTLSClientConfig: &tls.Config{InsecureSkipVerify: !ps.UpstreamVerifySSL},\n\t}\n\n\tif ps.UpstreamProxy != \"\" {\n\t\tproxyURL, err := url.Parse(ps.UpstreamProxy)\n\t\tif err == nil {\n\t\t\ttransport.Proxy = http.ProxyURL(proxyURL)\n\t\t}\n\t}\n\n\tclient := &http.Client{\n\t\tTimeout:   30 * time.Second,\n\t\tTransport: transport,\n\t\tCheckRedirect: func(req *http.Request, via []*http.Request) error {\n\t\t\treturn http.ErrUseLastResponse\n\t\t},\n\t}\n\n\tresp, err := client.Do(proxyReq)\n\tif err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusBadGateway)\n\t\treturn\n\t}\n\tdefer resp.Body.Close()\n\n\t// Copy response headers\n\tfor k, v := range resp.Header {\n\t\tw.Header()[k] = v\n\t}\n\n\tw.WriteHeader(resp.StatusCode)\n\tio.Copy(w, resp.Body)\n\n\tif ps.Verbose {\n\t\tstatus := \"✅\"\n\t\tif resp.StatusCode >= 400 {\n\t\t\tstatus = \"⚠️\"\n\t\t}\n\t\tfmt.Printf(\"   ↑ %s %d\\n\", status, resp.StatusCode)\n\t}\n}\n\nfunc (ps *ProxyServer) HandleCONNECT(w http.ResponseWriter, r *http.Request) {\n\tif ps.CACertPath == \"\" {\n\t\thttp.Error(w, \"HTTPS not supported\", http.StatusNotImplemented)\n\t\treturn\n\t}\n\n\thost := r.Host\n\thostname := strings.Split(host, \":\")[0]\n\n\tif ps.Verbose {\n\t\tfmt.Printf(\"\\n🔒 [CONNECT] %s\\n\", host)\n\t}\n\n\t// Get or generate certificate\n\tps.certMutex.RLock()\n\tcert, exists := ps.certCache[hostname]\n\tps.certMutex.RUnlock()\n\n\tif !exists {\n\t\tnewCert, err := generateHostCert(hostname, ps.CACertPath, ps.CAKeyPath)\n\t\tif err != nil {\n\t\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\t\tcert = newCert\n\n\t\tps.certMutex.Lock()\n\t\tps.certCache[hostname] = cert\n\t\tps.certMutex.Unlock()\n\t}\n\n\t// Send 200 Connection Established\n\tw.WriteHeader(http.StatusOK)\n\n\t// Hijack connection\n\thijacker, ok := w.(http.Hijacker)\n\tif !ok {\n\t\thttp.Error(w, \"Hijacking not supported\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tclientConn, _, err := hijacker.Hijack()\n\tif err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusServiceUnavailable)\n\t\treturn\n\t}\n\tdefer clientConn.Close()\n\n\t// Wrap with TLS\n\ttlsConfig := &tls.Config{\n\t\tCertificates: []tls.Certificate{*cert},\n\t}\n\n\ttlsConn := tls.Server(clientConn, tlsConfig)\n\tdefer tlsConn.Close()\n\n\tif err := tlsConn.Handshake(); err != nil {\n\t\tif ps.Verbose {\n\t\t\tfmt.Printf(\"✗ SSL handshake failed: %v\\n\", err)\n\t\t}\n\t\treturn\n\t}\n\n\t// Read HTTPS request\n\treader := bufio.NewReader(tlsConn)\n\treq, err := http.ReadRequest(reader)\n\tif err != nil {\n\t\treturn\n\t}\n\n\ttargetURL := \"https://\" + hostname + req.URL.String()\n\n\tif ps.IsBlacklisted(targetURL) {\n\t\tif ps.Verbose {\n\t\t\tfmt.Printf(\"   🚫 BLOCKED (blacklist): %s\\n\", targetURL)\n\t\t}\n\t\treturn\n\t}\n\n\tworkerURL := ps.GetWorkerURL()\n\tif workerURL == \"\" {\n\t\treturn\n\t}\n\n\tproxyURL := workerURL + \"?url=\" + url.QueryEscape(targetURL)\n\n\tif ps.Verbose {\n\t\tfmt.Printf(\"   📤 [%s] %s\\n\", req.Method, targetURL)\n\t\tfmt.Printf(\"      ↓ via Worker: %s\\n\", workerURL)\n\t}\n\n\t// Create proxy request\n\tproxyReq, err := http.NewRequest(req.Method, proxyURL, req.Body)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tfor k, v := range req.Header {\n\t\tif !strings.EqualFold(k, \"Host\") && !strings.EqualFold(k, \"Connection\") {\n\t\t\tproxyReq.Header[k] = v\n\t\t}\n\t}\n\n\t// Setup transport with optional upstream proxy\n\ttransport := &http.Transport{\n\t\tTLSClientConfig: &tls.Config{InsecureSkipVerify: !ps.UpstreamVerifySSL},\n\t}\n\n\tif ps.UpstreamProxy != \"\" {\n\t\tupstreamURL, err := url.Parse(ps.UpstreamProxy)\n\t\tif err == nil {\n\t\t\ttransport.Proxy = http.ProxyURL(upstreamURL)\n\t\t}\n\t}\n\n\tclient := &http.Client{\n\t\tTimeout:   30 * time.Second,\n\t\tTransport: transport,\n\t\tCheckRedirect: func(req *http.Request, via []*http.Request) error {\n\t\t\treturn http.ErrUseLastResponse\n\t\t},\n\t}\n\n\tresp, err := client.Do(proxyReq)\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer resp.Body.Close()\n\n\t// Write response\n\ttlsConn.Write([]byte(fmt.Sprintf(\"HTTP/1.1 %d %s\\r\\n\", resp.StatusCode, resp.Status)))\n\n\tfor k, v := range resp.Header {\n\t\tfor _, vv := range v {\n\t\t\ttlsConn.Write([]byte(fmt.Sprintf(\"%s: %s\\r\\n\", k, vv)))\n\t\t}\n\t}\n\n\ttlsConn.Write([]byte(\"\\r\\n\"))\n\tio.Copy(tlsConn, resp.Body)\n\n\tif ps.Verbose {\n\t\tstatus := \"✅\"\n\t\tif resp.StatusCode >= 400 {\n\t\t\tstatus = \"⚠️\"\n\t\t}\n\t\tfmt.Printf(\"      ↑ %s %d\\n\", status, resp.StatusCode)\n\t}\n}\n\nfunc (ps *ProxyServer) Start(blacklistFile string) error {\n\t// Setup SSL\n\tps.CACertPath = \"flaretunnel_ca.crt\"\n\tps.CAKeyPath = \"flaretunnel_ca.key\"\n\n\tif !ps.NoSSLIntercept {\n\t\tif err := generateCACert(ps.CACertPath, ps.CAKeyPath); err != nil {\n\t\t\tfmt.Printf(\"⚠️  SSL setup failed: %v\\n\", err)\n\t\t\tps.CACertPath = \"\"\n\t\t}\n\t} else {\n\t\tps.CACertPath = \"\"\n\t}\n\n\t// Load blacklist\n\tps.LoadBlacklist(blacklistFile)\n\n\tfmt.Println(\"\\n\" + strings.Repeat(\"=\", 80))\n\tfmt.Println(\"🚀 FlareTunnel Tunnel Server Started\")\n\tfmt.Println(strings.Repeat(\"=\", 80))\n\tfmt.Printf(\"📡 Listening: %s:%d\\n\", ps.Host, ps.Port)\n\tfmt.Printf(\"⚙️  Workers: %d\\n\", len(ps.Workers))\n\tfmt.Printf(\"🔄 Rotation: %s\\n\", ps.RotationMode)\n\n\tif ps.NoSSLIntercept {\n\t\tfmt.Printf(\"🔒 SSL/HTTPS: ✗ Disabled by --no-ssl-intercept\\n\")\n\t} else if ps.CACertPath != \"\" {\n\t\tfmt.Printf(\"🔒 SSL/HTTPS: ✓ Enabled (HTTPS CONNECT supported)\\n\")\n\t} else {\n\t\tfmt.Printf(\"🔒 SSL/HTTPS: ✗ Disabled\\n\")\n\t}\n\n\tif ps.AllowIPAccess {\n\t\tfmt.Printf(\"🛡️  IP Blocking: ❌ Disabled (--unsafe)\\n\")\n\t} else {\n\t\tfmt.Printf(\"🛡️  IP Blocking: ✅ Enabled (saves Worker requests)\\n\")\n\t}\n\n\tblacklistMsg := fmt.Sprintf(\"🚫 Blacklist: %d pattern(s)\", len(ps.BlacklistPatterns))\n\tif blacklistFile == \"blacklist-minimal.txt\" && len(ps.BlacklistPatterns) > 0 {\n\t\tblacklistMsg += \" ✅ (default: blacklist-minimal.txt)\"\n\t}\n\tfmt.Println(blacklistMsg)\n\n\tif ps.UpstreamProxy != \"\" {\n\t\tfmt.Printf(\"🔗 Upstream: %s\\n\", ps.UpstreamProxy)\n\t} else {\n\t\tfmt.Printf(\"🔗 Upstream: ❌ None (direct to Workers)\\n\")\n\t}\n\n\tif ps.Verbose {\n\t\tfmt.Printf(\"📝 Verbose: ✅ Enabled\\n\")\n\t} else {\n\t\tfmt.Printf(\"📝 Verbose: ❌ Disabled\\n\")\n\t}\n\n\tfmt.Println(\"\\n📋 Selected Workers:\")\n\tfor idx, worker := range ps.Workers {\n\t\tworkerName := worker.Name\n\t\tif len(workerName) > 50 {\n\t\t\tworkerName = workerName[:47] + \"...\"\n\t\t}\n\t\tfmt.Printf(\"   [%d] %s\\n\", idx, workerName)\n\t}\n\n\tif ps.UpstreamProxy != \"\" {\n\t\tfmt.Println(\"\\n🔀 Request Flow:\")\n\t\tfmt.Printf(\"   Client → FlareTunnel:%d → %s → Cloudflare Workers → Target\\n\", ps.Port, ps.UpstreamProxy)\n\t}\n\n\tfmt.Println(\"\\n⚙️  Proxy Configuration:\")\n\tfmt.Printf(\"   HTTP Proxy:  %s:%d\\n\", ps.Host, ps.Port)\n\tfmt.Printf(\"   HTTPS Proxy: %s:%d\\n\", ps.Host, ps.Port)\n\n\tif ps.CACertPath != \"\" {\n\t\tfmt.Println(\"\\n🔐 For HTTPS without warnings, install CA certificate:\")\n\t\tfmt.Printf(\"   📄 File: %s\\n\", ps.CACertPath)\n\t\tfmt.Printf(\"   💡 Or use verify=False in code\\n\")\n\t}\n\n\tfmt.Println(\"\\n💡 Tips:\")\n\tif len(ps.BlacklistPatterns) > 0 {\n\t\tfmt.Println(\"   • Blacklist active - saving Worker requests! 💰\")\n\t} else {\n\t\tfmt.Println(\"   • No blacklist - use --blacklist blacklist-minimal.txt to save requests\")\n\t}\n\tif !ps.AllowIPAccess {\n\t\tfmt.Println(\"   • IP blocking active - Cloudflare doesn't support IPs anyway\")\n\t}\n\tfmt.Println(\"   • Press Ctrl+C to stop\")\n\tfmt.Println(strings.Repeat(\"=\", 80))\n\tfmt.Println()\n\n\t// Start server\n\thandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tif r.Method == http.MethodConnect {\n\t\t\tps.HandleCONNECT(w, r)\n\t\t} else {\n\t\t\tps.HandleHTTP(w, r)\n\t\t}\n\t})\n\n\tserver := &http.Server{\n\t\tAddr:    fmt.Sprintf(\"%s:%d\", ps.Host, ps.Port),\n\t\tHandler: handler,\n\t}\n\n\treturn server.ListenAndServe()\n}\n\n// ====================================================================\n// UTILITY FUNCTIONS\n// ====================================================================\n\nfunc generateWorkerName() string {\n\ttimestamp := time.Now().Unix()\n\tsuffix := randomString(6)\n\treturn fmt.Sprintf(\"flaretunnel-%d-%s\", timestamp, suffix)\n}\n\nfunc randomString(n int) string {\n\tconst letters = \"abcdefghijklmnopqrstuvwxyz\"\n\tb := make([]byte, n)\n\tfor i := range b {\n\t\tb[i] = letters[time.Now().UnixNano()%int64(len(letters))]\n\t\ttime.Sleep(1 * time.Nanosecond)\n\t}\n\treturn string(b)\n}\n\nfunc isIPAddress(hostname string) bool {\n\thost := strings.Split(hostname, \":\")[0]\n\tip := net.ParseIP(host)\n\treturn ip != nil\n}\n\nfunc testWorker(workerURL string) bool {\n\ttestURL := workerURL + \"?url=\" + url.QueryEscape(\"https://httpbin.org/status/200\")\n\tclient := &http.Client{Timeout: 5 * time.Second}\n\tresp, err := client.Get(testURL)\n\tif err != nil {\n\t\treturn false\n\t}\n\tdefer resp.Body.Close()\n\treturn resp.StatusCode == 200\n}\n\nfunc max(a, b int) int {\n\tif a > b {\n\t\treturn a\n\t}\n\treturn b\n}\n\nfunc sumMap(m map[string]int) int {\n\tsum := 0\n\tfor _, v := range m {\n\t\tsum += v\n\t}\n\treturn sum\n}\n\n// Simple multipart writer\ntype simpleMultipartWriter struct {\n\tbuf       *bytes.Buffer\n\tboundary  string\n}\n\nfunc multipartWriter(buf *bytes.Buffer) *simpleMultipartWriter {\n\treturn &simpleMultipartWriter{\n\t\tbuf:      buf,\n\t\tboundary: \"----FlareTunnelBoundary\" + randomString(16),\n\t}\n}\n\nfunc (w *simpleMultipartWriter) WriteField(field, value string) {\n\tw.buf.WriteString(\"--\" + w.boundary + \"\\r\\n\")\n\tw.buf.WriteString(fmt.Sprintf(\"Content-Disposition: form-data; name=\\\"%s\\\"\\r\\n\\r\\n\", field))\n\tw.buf.WriteString(value + \"\\r\\n\")\n}\n\nfunc (w *simpleMultipartWriter) Close() {\n\tw.buf.WriteString(\"--\" + w.boundary + \"--\\r\\n\")\n}\n\nfunc (w *simpleMultipartWriter) FormDataContentType() string {\n\treturn \"multipart/form-data; boundary=\" + w.boundary\n}\n\n// ====================================================================\n// CLI COMMANDS\n// ====================================================================\n\nfunc setupConfig() error {\n\tfmt.Println(strings.Repeat(\"=\", 70))\n\tfmt.Println(\"🔧 FlareTunnel Multi-Account Configuration\")\n\tfmt.Println(strings.Repeat(\"=\", 70))\n\tfmt.Println()\n\tfmt.Println(\"Getting Cloudflare Credentials:\")\n\tfmt.Println(\"1. Sign up at https://cloudflare.com\")\n\tfmt.Println(\"2. Go to https://dash.cloudflare.com/profile/api-tokens\")\n\tfmt.Println(\"3. Click Create Token - use 'Edit Cloudflare Workers' template\")\n\tfmt.Println(\"4. Set account and zone resources to all\")\n\tfmt.Println(\"5. Copy the token and Account ID\")\n\tfmt.Println()\n\tfmt.Println(\"💡 Tip: You can add multiple accounts for more quota!\")\n\tfmt.Println(\"   Each account = 100,000 requests/day\")\n\tfmt.Println()\n\n\taccounts := []Account{}\n\treader := bufio.NewReader(os.Stdin)\n\n\tfor accountNum := 1; ; accountNum++ {\n\t\tfmt.Printf(\"\\n📋 Account #%d\\n\", accountNum)\n\t\tfmt.Println(strings.Repeat(\"-\", 40))\n\n\t\tfmt.Printf(\"Account name (e.g., 'main', 'backup') [account-%d]: \", accountNum)\n\t\taccountName, _ := reader.ReadString('\\n')\n\t\taccountName = strings.TrimSpace(accountName)\n\t\tif accountName == \"\" {\n\t\t\taccountName = fmt.Sprintf(\"account-%d\", accountNum)\n\t\t}\n\n\t\tfmt.Print(\"API token: \")\n\t\tapiToken, _ := reader.ReadString('\\n')\n\t\tapiToken = strings.TrimSpace(apiToken)\n\t\tif apiToken == \"\" {\n\t\t\tif accountNum == 1 {\n\t\t\t\tfmt.Println(\"❌ API token is required\")\n\t\t\t\treturn fmt.Errorf(\"no API token provided\")\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\n\t\tfmt.Print(\"Account ID: \")\n\t\taccountID, _ := reader.ReadString('\\n')\n\t\taccountID = strings.TrimSpace(accountID)\n\t\tif accountID == \"\" {\n\t\t\tif accountNum == 1 {\n\t\t\t\tfmt.Println(\"❌ Account ID is required\")\n\t\t\t\treturn fmt.Errorf(\"no account ID provided\")\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\n\t\taccounts = append(accounts, Account{\n\t\t\tName:      accountName,\n\t\t\tAPIToken:  apiToken,\n\t\t\tAccountID: accountID,\n\t\t})\n\n\t\tfmt.Printf(\"✅ Account '%s' added!\\n\", accountName)\n\n\t\tif accountNum >= 1 {\n\t\t\tfmt.Print(\"\\nAdd another account? (y/N): \")\n\t\t\taddMore, _ := reader.ReadString('\\n')\n\t\t\tif strings.ToLower(strings.TrimSpace(addMore)) != \"y\" {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\tif len(accounts) == 0 {\n\t\tfmt.Println(\"❌ No accounts configured\")\n\t\treturn fmt.Errorf(\"no accounts configured\")\n\t}\n\n\tconfig := Config{Accounts: accounts}\n\tdata, _ := json.MarshalIndent(config, \"\", \"  \")\n\n\tif err := os.WriteFile(\"flaretunnel.json\", data, 0644); err != nil {\n\t\tfmt.Printf(\"❌ Error saving configuration: %v\\n\", err)\n\t\treturn err\n\t}\n\n\tfmt.Println()\n\tfmt.Println(strings.Repeat(\"=\", 70))\n\tfmt.Println(\"✅ Configuration saved!\")\n\tfmt.Println(strings.Repeat(\"=\", 70))\n\tfmt.Println(\"📄 Config file: flaretunnel.json\")\n\tfmt.Printf(\"📊 Accounts configured: %d\\n\", len(accounts))\n\tfmt.Printf(\"🚀 Total daily quota: %d requests\\n\", len(accounts)*100000)\n\tfmt.Println()\n\n\tfor _, acc := range accounts {\n\t\tfmt.Printf(\"   • %s: %s...\\n\", acc.Name, acc.AccountID[:8])\n\t}\n\n\tfmt.Println()\n\tfmt.Println(\"🎉 FlareTunnel is now configured!\")\n\n\treturn nil\n}\n\nfunc parseWorkerIndices(indicesStr string) []int {\n\tindices := []int{}\n\tparts := strings.Split(indicesStr, \",\")\n\n\tfor _, part := range parts {\n\t\tpart = strings.TrimSpace(part)\n\t\tif strings.Contains(part, \"-\") {\n\t\t\trangeParts := strings.Split(part, \"-\")\n\t\t\tif len(rangeParts) == 2 {\n\t\t\t\tstart, err1 := strconv.Atoi(strings.TrimSpace(rangeParts[0]))\n\t\t\t\tend, err2 := strconv.Atoi(strings.TrimSpace(rangeParts[1]))\n\t\t\t\tif err1 == nil && err2 == nil {\n\t\t\t\t\tfor i := start; i <= end; i++ {\n\t\t\t\t\t\tindices = append(indices, i)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif idx, err := strconv.Atoi(part); err == nil {\n\t\t\t\tindices = append(indices, idx)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn indices\n}\n\n// ====================================================================\n// MAIN\n// ====================================================================\n\nfunc main() {\n\tif len(os.Args) < 2 {\n\t\tprintHelp()\n\t\treturn\n\t}\n\n\tcommand := os.Args[1]\n\n\tswitch command {\n\tcase \"config\":\n\t\tsetupConfig()\n\n\tcase \"create\":\n\t\tcount := 1\n\t\taccountName := \"\"\n\t\tdistribute := false\n\n\t\tfor i := 2; i < len(os.Args); i++ {\n\t\t\tswitch os.Args[i] {\n\t\t\tcase \"--count\":\n\t\t\t\tif i+1 < len(os.Args) {\n\t\t\t\t\tcount, _ = strconv.Atoi(os.Args[i+1])\n\t\t\t\t\ti++\n\t\t\t\t}\n\t\t\tcase \"--account\":\n\t\t\t\tif i+1 < len(os.Args) {\n\t\t\t\t\taccountName = os.Args[i+1]\n\t\t\t\t\ti++\n\t\t\t\t}\n\t\t\tcase \"--distribute\":\n\t\t\t\tdistribute = true\n\t\t\t}\n\t\t}\n\n\t\tft, err := NewFlareTunnel(\"flaretunnel.json\")\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"❌ Configuration error: %v\\n\", err)\n\t\t\tfmt.Println(\"Run: go run FlareTunnel.go config\")\n\t\t\treturn\n\t\t}\n\n\t\tft.CreateWorkers(count, accountName, distribute)\n\n\tcase \"list\":\n\t\tverbose := false\n\t\tcheckStatus := false\n\n\t\tfor i := 2; i < len(os.Args); i++ {\n\t\t\tswitch os.Args[i] {\n\t\t\tcase \"--verbose\", \"-v\":\n\t\t\t\tverbose = true\n\t\t\tcase \"--status\":\n\t\t\t\tcheckStatus = true\n\t\t\t}\n\t\t}\n\n\t\t// If both flags provided, verbose already includes status check\n\t\tif verbose && checkStatus {\n\t\t\tfmt.Println(\"⚠️  Note: --verbose already includes live status check, --status flag is redundant\\n\")\n\t\t\t// checkStatus is already covered by verbose, no need to set it\n\t\t}\n\n\t\tft, err := NewFlareTunnel(\"flaretunnel.json\")\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"❌ Configuration error: %v\\n\", err)\n\t\t\treturn\n\t\t}\n\n\t\tft.ListWorkers(verbose, checkStatus)\n\n\tcase \"test\":\n\t\ttargetURL := \"https://ifconfig.me/ip\"\n\t\tmethod := \"GET\"\n\n\t\tfor i := 2; i < len(os.Args); i++ {\n\t\t\tswitch os.Args[i] {\n\t\t\tcase \"--url\":\n\t\t\t\tif i+1 < len(os.Args) {\n\t\t\t\t\ttargetURL = os.Args[i+1]\n\t\t\t\t\ti++\n\t\t\t\t}\n\t\t\tcase \"--method\":\n\t\t\t\tif i+1 < len(os.Args) {\n\t\t\t\t\tmethod = os.Args[i+1]\n\t\t\t\t\ti++\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tft, err := NewFlareTunnel(\"flaretunnel.json\")\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"❌ Configuration error: %v\\n\", err)\n\t\t\treturn\n\t\t}\n\n\t\tft.TestWorkers(targetURL, method)\n\n\tcase \"export\":\n\t\toutputFile := \"flaretunnel_config_backup.json\"\n\n\t\tfor i := 2; i < len(os.Args); i++ {\n\t\t\tswitch os.Args[i] {\n\t\t\tcase \"--output\":\n\t\t\t\tif i+1 < len(os.Args) {\n\t\t\t\t\toutputFile = os.Args[i+1]\n\t\t\t\t\ti++\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tft, _ := NewFlareTunnel(\"flaretunnel.json\")\n\t\tft.ExportConfig(outputFile)\n\n\tcase \"import\":\n\t\tinputFile := \"\"\n\t\tmerge := false\n\n\t\tfor i := 2; i < len(os.Args); i++ {\n\t\t\tswitch os.Args[i] {\n\t\t\tcase \"--input\":\n\t\t\t\tif i+1 < len(os.Args) {\n\t\t\t\t\tinputFile = os.Args[i+1]\n\t\t\t\t\ti++\n\t\t\t\t}\n\t\t\tcase \"--merge\":\n\t\t\t\tmerge = true\n\t\t\t}\n\t\t}\n\n\t\tif inputFile == \"\" {\n\t\t\tfmt.Println(\"❌ Error: --input required for import command\")\n\t\t\tfmt.Println(\"Usage: go run FlareTunnel.go import --input config_backup.json [--merge]\")\n\t\t\treturn\n\t\t}\n\n\t\tft := &FlareTunnel{}\n\t\tft.ImportConfig(inputFile, merge)\n\n\tcase \"cleanup\":\n\t\taccountName := \"\"\n\n\t\tfor i := 2; i < len(os.Args); i++ {\n\t\t\tswitch os.Args[i] {\n\t\t\tcase \"--account\":\n\t\t\t\tif i+1 < len(os.Args) {\n\t\t\t\t\taccountName = os.Args[i+1]\n\t\t\t\t\ti++\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tft, err := NewFlareTunnel(\"flaretunnel.json\")\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"❌ Configuration error: %v\\n\", err)\n\t\t\treturn\n\t\t}\n\n\t\treader := bufio.NewReader(os.Stdin)\n\t\tif accountName != \"\" {\n\t\t\tfmt.Printf(\"Delete ALL workers from account '%s'? (y/N): \", accountName)\n\t\t} else {\n\t\t\tfmt.Print(\"Delete ALL FlareTunnel endpoints from ALL accounts? (y/N): \")\n\t\t}\n\n\t\tconfirm, _ := reader.ReadString('\\n')\n\t\tif strings.ToLower(strings.TrimSpace(confirm)) == \"y\" {\n\t\t\tft.CleanupWorkers(accountName)\n\t\t} else {\n\t\t\tfmt.Println(\"Cleanup cancelled.\")\n\t\t}\n\n\tcase \"tunnel\":\n\t\thost := \"127.0.0.1\"\n\t\tport := 8080\n\t\tworkersStr := \"\"\n\t\tmode := \"round-robin\"\n\t\tverbose := false\n\t\tunsafe := false\n\t\tblacklist := \"blacklist-minimal.txt\"\n\t\tupstreamProxy := \"\"\n\t\tupstreamVerifySSL := false\n\t\tcacheCerts := false\n\t\tnoSSLIntercept := false\n\t\tblockPatterns := []string{}\n\n\t\tfor i := 2; i < len(os.Args); i++ {\n\t\t\tswitch os.Args[i] {\n\t\t\tcase \"--host\":\n\t\t\t\tif i+1 < len(os.Args) {\n\t\t\t\t\thost = os.Args[i+1]\n\t\t\t\t\ti++\n\t\t\t\t}\n\t\t\tcase \"--port\":\n\t\t\t\tif i+1 < len(os.Args) {\n\t\t\t\t\tport, _ = strconv.Atoi(os.Args[i+1])\n\t\t\t\t\ti++\n\t\t\t\t}\n\t\t\tcase \"--workers\":\n\t\t\t\tif i+1 < len(os.Args) {\n\t\t\t\t\tworkersStr = os.Args[i+1]\n\t\t\t\t\ti++\n\t\t\t\t}\n\t\t\tcase \"--mode\":\n\t\t\t\tif i+1 < len(os.Args) {\n\t\t\t\t\tmode = os.Args[i+1]\n\t\t\t\t\ti++\n\t\t\t\t}\n\t\t\tcase \"--verbose\", \"-v\":\n\t\t\t\tverbose = true\n\t\t\tcase \"--unsafe\":\n\t\t\t\tunsafe = true\n\t\t\tcase \"--blacklist\":\n\t\t\t\tif i+1 < len(os.Args) {\n\t\t\t\t\tblacklist = os.Args[i+1]\n\t\t\t\t\ti++\n\t\t\t\t}\n\t\t\tcase \"--upstream-proxy\":\n\t\t\t\tif i+1 < len(os.Args) {\n\t\t\t\t\tupstreamProxy = os.Args[i+1]\n\t\t\t\t\ti++\n\t\t\t\t}\n\t\t\tcase \"--upstream-verify-ssl\":\n\t\t\t\tupstreamVerifySSL = true\n\t\t\tcase \"--cache-certs\":\n\t\t\t\tcacheCerts = true\n\t\t\tcase \"--no-ssl-intercept\":\n\t\t\t\tnoSSLIntercept = true\n\t\t\tcase \"--block\":\n\t\t\t\tif i+1 < len(os.Args) {\n\t\t\t\t\tblockPatterns = append(blockPatterns, os.Args[i+1])\n\t\t\t\t\ti++\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tps := NewProxyServer(host, port)\n\t\tps.RotationMode = mode\n\t\tps.Verbose = verbose\n\t\tps.AllowIPAccess = unsafe\n\t\tps.UpstreamProxy = upstreamProxy\n\t\tps.UpstreamVerifySSL = upstreamVerifySSL\n\t\tps.CacheCerts = cacheCerts\n\t\tps.NoSSLIntercept = noSSLIntercept\n\n\t\t// Add inline block patterns\n\t\tif len(blockPatterns) > 0 {\n\t\t\tps.InlineBlockPatterns = blockPatterns\n\t\t}\n\n\t\tvar workerIndices []int\n\t\tif workersStr != \"\" {\n\t\t\tworkerIndices = parseWorkerIndices(workersStr)\n\t\t}\n\n\t\tif err := ps.LoadWorkers(\"flaretunnel_endpoints.json\", workerIndices); err != nil {\n\t\t\tfmt.Printf(\"❌ Failed to load workers: %v\\n\", err)\n\t\t\tfmt.Println(\"Run: go run FlareTunnel.go create --count 5\")\n\t\t\treturn\n\t\t}\n\n\t\tif len(ps.Workers) == 0 {\n\t\t\tfmt.Println(\"❌ No workers available\")\n\t\t\treturn\n\t\t}\n\n\t\tps.Start(blacklist)\n\n\tdefault:\n\t\tprintHelp()\n\t}\n}\n\nfunc printHelp() {\n\thelp := `\nFlareTunnel - Cloudflare Workers Proxy System (Go Version)\n\nUsage: go run FlareTunnel.go <command> [options]\n\nCommands:\n  config                    Configure Cloudflare API credentials (interactive)\n  create                    Create new Cloudflare Workers\n  list                      List all Workers with analytics\n  test                      Test Workers with a target URL\n  export                    Export configuration to backup file\n  import                    Import configuration from backup file\n  cleanup                   Delete Workers (prompts for confirmation)\n  tunnel                    Start local proxy server\n\nExamples:\n  # Configuration\n  go run FlareTunnel.go config\n\n  # Create Workers\n  go run FlareTunnel.go create --count 5\n  go run FlareTunnel.go create --count 10 --distribute\n  go run FlareTunnel.go create --count 3 --account main\n\n  # List Workers\n  go run FlareTunnel.go list           # Basic list\n  go run FlareTunnel.go list --verbose # Detailed + live status check\n  go run FlareTunnel.go list --status  # Only live response times\n\n  # Test Workers\n  go run FlareTunnel.go test\n  go run FlareTunnel.go test --url https://httpbin.org/ip\n  go run FlareTunnel.go test --url https://example.com --method POST\n\n  # Export/Import Config\n  go run FlareTunnel.go export --output my_backup.json\n  go run FlareTunnel.go import --input my_backup.json\n  go run FlareTunnel.go import --input config.json --merge\n\n  # Start Tunnel\n  go run FlareTunnel.go tunnel --verbose\n  go run FlareTunnel.go tunnel --workers 0,1,2 --mode random\n  go run FlareTunnel.go tunnel --port 9090 --blacklist blacklist.txt\n  go run FlareTunnel.go tunnel --upstream-proxy http://127.0.0.1:8888\n\n  # Cleanup\n  go run FlareTunnel.go cleanup\n  go run FlareTunnel.go cleanup --account main\n\nOptions:\n  Create:\n    --count N               Number of workers to create (default: 1)\n    --account NAME          Create on specific account\n    --distribute            Auto-distribute across accounts based on quota\n\n  List:\n    --verbose, -v           Show detailed info + live status check (created, age, response times)\n    --status                Check only live worker status (response times, no verbose info)\n\n  Test:\n    --url URL               Target URL to test (default: https://ifconfig.me/ip)\n    --method METHOD         HTTP method (default: GET)\n\n  Export:\n    --output FILE           Output file (default: flaretunnel_config_backup.json)\n\n  Import:\n    --input FILE            Input config backup file (REQUIRED)\n    --merge                 Merge with existing config (skip duplicates)\n\n  Cleanup:\n    --account NAME          Delete from specific account only\n\n  Tunnel:\n    --host HOST             Bind host (default: 127.0.0.1)\n    --port PORT             Bind port (default: 8080)\n    --workers INDICES       Worker indices (e.g., '0,1,2' or '0-2')\n    --mode MODE             Rotation mode: random, round-robin (default: round-robin)\n    --verbose, -v           Enable verbose logging\n    --unsafe                Allow IP access (not recommended)\n    --upstream-proxy URL    Upstream proxy (e.g., http://127.0.0.1:8080)\n    --upstream-verify-ssl   Verify SSL for upstream proxy\n    --cache-certs           Cache SSL certs to disk\n    --no-ssl-intercept      Disable SSL interception\n    --blacklist FILE        Blacklist file (default: blacklist-minimal.txt)\n    --block PATTERN         Block pattern (can be used multiple times)\n`\n\tfmt.Println(help)\n}\n\n"
  },
  {
    "path": "README.md",
    "content": "# 🚀 FlareTunnel\r\n\r\n<div align=\"center\">\r\n\r\n<img src=\"logo.png\" alt=\"FlareTunnel\" width=\"300\">\r\n\r\n**A unified proxy system that routes traffic through Cloudflare Workers for IP rotation and anonymity**\r\n\r\n```\r\nClient → FlareTunnel (local) → Cloudflare Workers → Target Website\r\n```\r\n\r\n![FlareTunnel Banner](https://img.shields.io/badge/FlareTunnel-v1.0.0-red?style=for-the-badge&logo=security&logoColor=white)\r\n![Go](https://img.shields.io/badge/Go-1.19+-00ADD8?style=for-the-badge&logo=go&logoColor=white)\r\n![License](https://img.shields.io/badge/License-Non--Commercial-orange?style=for-the-badge)\r\n</div>\r\n\r\n**FlareTunnel** is a powerful, unified proxy system that leverages **Cloudflare Workers** to create a robust, rotating proxy network. It allows you to route your traffic through Cloudflare's global edge network, providing high anonymity, speed, and reliability.\r\n\r\n## ✨ Features\r\n\r\n*   **🌐 Unlimited Rotating Proxies**: Automatically deploy and manage multiple Cloudflare Workers as proxy endpoints.\r\n*   **🔄 Smart Load Balancing**: Distributes traffic across your workers using Random or Round-Robin strategies.\r\n*   **⚡ High Performance**: Uses Cloudflare's global edge network for low latency.\r\n*   **🔐 SSL/HTTPS Support**: Full support for HTTPS traffic with optional SSL interception for deep inspection.\r\n*   **👥 Multi-Account Support**: seamless management of multiple Cloudflare accounts to maximize request quotas (100k requests/day per account).\r\n*   **🛡️ Ad & Tracker Blocking**: Built-in blacklist system to block unwanted traffic and save worker quotas.\r\n*   **📊 Analytics**: Real-time usage statistics and quota tracking per account.\r\n\r\n## 🏗️ Architecture\r\n\r\n```mermaid\r\ngraph LR\r\n    Client[\"Client (Browser/App)\"] -->|HTTP/HTTPS| LocalProxy[\"Local Proxy :8080\"]\r\n    subgraph FlareTunnel System\r\n        LocalProxy -->|Load Balancing| Rotator{Worker Rotator}\r\n        Rotator -->|Request A| W1[Worker 1]\r\n        Rotator -->|Request B| W2[Worker 2]\r\n        Rotator -->|Request C| W3[Worker 3]\r\n    end\r\n    W1 -->|Fetch| Target[Target Website]\r\n    W2 -->|Fetch| Target\r\n    W3 -->|Fetch| Target\r\n    style LocalProxy fill:#7289da,stroke:#333,stroke-width:2px,color:white\r\n    style W1 fill:#f38020,stroke:#333,stroke-width:2px,color:white\r\n    style W2 fill:#f38020,stroke:#333,stroke-width:2px,color:white\r\n    style W3 fill:#f38020,stroke:#333,stroke-width:2px,color:white\r\n    style Client fill:#fff,stroke:#333,stroke-width:2px\r\n    style Target fill:#fff,stroke:#333,stroke-width:2px\r\n```\r\n\r\n## 📦 Installation\r\n\r\n### Build from Source\r\n```bash\r\ngit clone https://github.com/MorDavid/FlareTunnel.git\r\ncd FlareTunnel\r\ngo build -o FlareTunnel FlareTunnel.go\r\n```\r\n\r\n## 🚀 Usage\r\n\r\n### 1. Configuration\r\nFirst, set up your Cloudflare credentials. You'll need your Account ID and an API Token (with \"Edit Cloudflare Workers\" permission).\r\n\r\n```bash\r\n./FlareTunnel config\r\n```\r\n\r\n### 2. Create Proxies\r\nDeploy new workers to your Cloudflare account.\r\n\r\n```bash\r\n# Create 5 new proxy workers\r\n./FlareTunnel create --count 5\r\n```\r\n\r\n### 3. Start the Tunnel\r\nStart the local proxy server. By default, it runs on `localhost:8080`.\r\n\r\n```bash\r\n./FlareTunnel tunnel\r\n```\r\n\r\nNow configure your browser or application to use the proxy:\r\n*   **Host**: `127.0.0.1`\r\n*   **Port**: `8080`\r\n\r\n## 🛠️ Commands Reference\r\n\r\n| Command | Description |\r\n|---------|-------------|\r\n| `config` | Configure Cloudflare API credentials (supports multiple accounts) |\r\n| `create` | Deploy new Worker proxies |\r\n| `list` | List all active proxies and show usage stats |\r\n| `tunnel` | Start the local proxy server |\r\n| `test` | Test connectivity of your proxies |\r\n| `cleanup` | Delete all workers from your account |\r\n\r\n## 📖 Basic Usage\r\n\r\n### Browser Configuration\r\n```\r\nHTTP Proxy:  127.0.0.1:8080\r\nHTTPS Proxy: 127.0.0.1:8080\r\n```\r\n\r\n### Python\r\n```python\r\nimport requests\r\nimport urllib3\r\nurllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)\r\n\r\nproxies = {\r\n    'http': 'http://127.0.0.1:8080',\r\n    'https': 'http://127.0.0.1:8080'\r\n}\r\n\r\nr = requests.get(\"https://httpbin.org/ip\", \r\n                 proxies=proxies, \r\n                 verify=False)\r\n\r\nprint(r.json()['origin'])  # Cloudflare Worker IP\r\n```\r\n\r\n### Quick Test\r\n```bash\r\n./FlareTunnel test\r\n```\r\n\r\n---\r\n\r\n## 🎯 Common Commands\r\n\r\n```bash\r\n# Worker Management\r\n./FlareTunnel list                    # List all workers\r\n./FlareTunnel list --verbose          # Detailed view (created, age, live status)\r\n./FlareTunnel list --status           # Check worker response times\r\n./FlareTunnel test                    # Test workers\r\n./FlareTunnel cleanup                 # Delete workers from ALL accounts\r\n./FlareTunnel cleanup --account main  # Delete workers from 'main' only\r\n\r\n# Multi-Account Worker Creation\r\n./FlareTunnel create --count 10 --distribute    # Auto-distribute based on quota\r\n./FlareTunnel create --count 5 --account main   # Create on specific account\r\n\r\n# Configuration Backup & Restore\r\n./FlareTunnel export                          # Export config (accounts + credentials)\r\n./FlareTunnel import --input config.json      # Import config (replace)\r\n./FlareTunnel import --input config.json --merge  # Merge with existing\r\n\r\n# Tunnel (Proxy Server)\r\n./FlareTunnel tunnel --verbose        # Basic\r\n./FlareTunnel tunnel --workers 0-2    # Specific workers\r\n./FlareTunnel tunnel --mode random    # Random rotation\r\n\r\n# With Blacklist (Recommended!)\r\n./FlareTunnel tunnel --verbose        # Default: blacklist-minimal.txt\r\n./FlareTunnel tunnel --blacklist blacklist.txt --verbose\r\n\r\n# With Burp Suite\r\n./FlareTunnel tunnel --port 9090 --upstream-proxy http://127.0.0.1:8080 --verbose\r\n```\r\n\r\n---\r\n\r\n## 💡 Blacklist System\r\n\r\n### blacklist-minimal.txt (Default) ⚡\r\n```\r\n✅ Analytics (google-analytics, mixpanel)\r\n✅ Images (.jpg, .png, .gif, etc.)\r\n✅ Fonts (.woff, .ttf, etc.)\r\n✅ Source maps (.map)\r\n\r\nSaves: ~30-40% Worker requests\r\nWebsite: Works perfectly in browser\r\n```\r\n\r\n### blacklist.txt (Full) 🔥\r\n```\r\n✅ Everything in minimal\r\n✅ Advertising\r\n✅ Social tracking\r\n✅ CSS/JS files\r\n✅ CDN libraries\r\n\r\nSaves: ~60-70% Worker requests\r\nWebsite: May look broken (missing assets)\r\n```\r\n\r\n### blacklist-aggressive.txt (Maximum) 💪\r\n```\r\n✅ Everything in full\r\n✅ Almost everything except HTML/API\r\n\r\nSaves: ~80-90% Worker requests\r\nWebsite: Will break in browser (automation tools only)\r\n```\r\n\r\n---\r\n## 🌟 Star History\r\n\r\n[![Star History Chart](https://api.star-history.com/svg?repos=MorDavid/FlareTunnel&type=date&legend=top-left)](https://www.star-history.com/#MorDavid/FlareTunnel&type=date&legend=top-left)\r\n\r\n## ⚠️ Disclaimer\r\n\r\nThis tool is for educational and research purposes only. Please respect Cloudflare's Terms of Service. The authors are not responsible for any misuse of this tool.\r\n\r\n**Made with ❤️ for the security and automation community**\r\n\r\n"
  },
  {
    "path": "blacklist-aggressive.txt",
    "content": "# FlareTunnel Blacklist - Aggressive Version 🔥💪\r\n# Blocks almost everything except HTML/API requests\r\n# Useful for automation tools (scrapers, bruteforce, etc.)\r\n# Website will look broken in browsers - but you'll save hundreds of thousands of Worker requests!\r\n\r\n# ========================================\r\n# 📊 Analytics & Tracking\r\n# ========================================\r\ngoogle-analytics.com\r\nwww.google-analytics.com\r\nssl.google-analytics.com\r\nanalytics.google.com\r\nstats.g.doubleclick.net\r\ndoubleclick.net\r\ngoogletagmanager.com\r\ngoogletagservices.com\r\nfacebook.com/tr\r\nconnect.facebook.net\r\ngraph.facebook.com\r\npx.ads.linkedin.com\r\nanalytics.linkedin.com\r\nanalytics.tiktok.com\r\nbusiness.tiktok.com\r\nads.tiktok.com\r\nlog.byteoversea.com\r\nhotjar.com\r\nstatic.hotjar.com\r\nscript.hotjar.com\r\napi.mixpanel.com\r\ndecide.mixpanel.com\r\nsegment.com\r\ncdn.segment.com\r\napi.segment.io\r\nomtrdc.net\r\nadobedc.net\r\ndemdex.net\r\ntrack.hubspot.com\r\napi.hubspot.com\r\nfullstory.com\r\nscript.crazyegg.com\r\njs-agent.newrelic.com\r\nbam.nr-data.net\r\ndatadoghq-browser-agent.com\r\nbrowser-intake-datadoghq.com\r\nsentry.io\r\nbrowser.sentry-cdn.com\r\ncriteo.com\r\ntaboola.com\r\noutbrain.com\r\nmixpanel.com\r\namplitude.com\r\nheap.io\r\nlogrocket.com\r\nnewrelic.com\r\nquantcast.com\r\nclarity.ms\r\ndatadoghq.com\r\npendo.io\r\nmouseflow.com\r\ncrazyegg.com\r\noptimizely.com\r\nkissmetrics.com\r\nchartbeat.com\r\n\r\n# ========================================\r\n# 📢 Advertising\r\n# ========================================\r\ngooglesyndication.com\r\ngoogleadservices.com\r\nadnxs.com\r\ncriteo.com\r\noutbrain.com\r\ntaboola.com\r\nmedia.net\r\npubmatic.com\r\nrubiconproject.com\r\nadroll.com\r\nadsystem.com\r\n\r\n# ========================================\r\n# 🖼️ ALL Images\r\n# ========================================\r\n.jpg\r\n.jpeg\r\n.png\r\n.gif\r\n.webp\r\n.ico\r\n.svg\r\n.bmp\r\n.tiff\r\n.avif\r\n\r\n# ========================================\r\n# 🎨 ALL CSS & Fonts\r\n# ========================================\r\n.css\r\n.woff\r\n.woff2\r\n.ttf\r\n.eot\r\n.otf\r\nfonts.googleapis.com\r\nfonts.gstatic.com\r\n\r\n# ========================================\r\n# 📦 ALL JavaScript\r\n# ========================================\r\n.js\r\n.map\r\n.min.js\r\n.bundle.js\r\najax.googleapis.com\r\ncdnjs.cloudflare.com\r\ncdn.jsdelivr.net\r\nunpkg.com\r\n\r\n# ========================================\r\n# 🎬 ALL Media\r\n# ========================================\r\n.mp4\r\n.webm\r\n.ogg\r\n.mp3\r\n.wav\r\n.m4a\r\n\r\n# ========================================\r\n# 📄 Documents\r\n# ========================================\r\n.pdf\r\n.zip\r\n.rar\r\n.doc\r\n.docx\r\n.xls\r\n.xlsx\r\n\r\n# ========================================\r\n# 👥 Social Media\r\n# ========================================\r\nfacebook.com/plugins\r\ntwitter.com/widgets\r\nplatform.twitter.com\r\nlinkedin.com/widgets\r\ninstagram.com/embed\r\npinterest.com\r\ntiktok.com/embed\r\n\r\n# ========================================\r\n# 🔌 ALL Widgets\r\n# ========================================\r\ndisqus.com\r\nlivechatinc.com\r\ntawk.to\r\nintercom.io\r\ndrift.com\r\nzendesk.com\r\n/chat\r\n/livechat\r\n/widget\r\n\r\n# ========================================\r\n# 🎯 Tracking Paths\r\n# ========================================\r\n/pixel\r\n/track\r\n/analytics\r\n/beacon\r\n/log\r\n/collect\r\n/event\r\n/_ga\r\n/_gat\r\n/gtag\r\n/impression\r\n\r\n"
  },
  {
    "path": "blacklist-minimal.txt",
    "content": "# ========================================\r\n# 📊 Analytics (Saves the most!)\r\n# ========================================\r\ngoogle-analytics.com\r\nwww.google-analytics.com\r\nssl.google-analytics.com\r\nanalytics.google.com\r\nstats.g.doubleclick.net\r\ndoubleclick.net\r\ngoogletagmanager.com\r\ngoogletagservices.com\r\nfacebook.com/tr\r\nconnect.facebook.net\r\ngraph.facebook.com\r\npx.ads.linkedin.com\r\nanalytics.linkedin.com\r\nanalytics.tiktok.com\r\nbusiness.tiktok.com\r\nads.tiktok.com\r\nlog.byteoversea.com\r\nhotjar.com\r\nstatic.hotjar.com\r\nscript.hotjar.com\r\napi.mixpanel.com\r\ndecide.mixpanel.com\r\nsegment.com\r\ncdn.segment.com\r\napi.segment.io\r\nomtrdc.net\r\nadobedc.net\r\ndemdex.net\r\ntrack.hubspot.com\r\napi.hubspot.com\r\nfullstory.com\r\nscript.crazyegg.com\r\njs-agent.newrelic.com\r\nbam.nr-data.net\r\ndatadoghq-browser-agent.com\r\nbrowser-intake-datadoghq.com\r\nsentry.io\r\nbrowser.sentry-cdn.com\r\ncriteo.com\r\ntaboola.com\r\noutbrain.com\r\n\r\n# ========================================\r\n# 🎨 Fonts\r\n# ========================================\r\nfonts.googleapis.com\r\nfonts.gstatic.com\r\nuse.typekit.net\r\nuse.fontawesome.com\r\n"
  },
  {
    "path": "blacklist.txt",
    "content": "# FlareTunnel Blacklist - Full Version 🔥\r\n# Save Worker Requests! 💰\r\n# Each line = one pattern. Lines with # = comments\r\n# Patterns checked against: hostname, path, extension, full URL\r\n\r\n# ========================================\r\n# 📊 Analytics & Tracking (Saves a lot!)\r\n# ========================================\r\ngoogle-analytics.com\r\ngoogletagmanager.com\r\nanalytics.google.com\r\ndoubleclick.net\r\nstats.g.doubleclick.net\r\nfacebook.com/tr\r\nconnect.facebook.net\r\nhotjar.com\r\nstatic.hotjar.com\r\nmixpanel.com\r\ncdn.mxpnl.com\r\nsegment.com\r\ncdn.segment.com\r\namplitude.com\r\nheap.io\r\nfullstory.com\r\nlogrocket.com\r\nlogrocket.io\r\nnewrelic.com\r\njs-agent.newrelic.com\r\nquantcast.com\r\nsentry.io\r\nsentry-cdn.com\r\nclarity.ms\r\ndatadoghq.com\r\npendo.io\r\nmouseflow.com\r\ncrazyegg.com\r\noptimizely.com\r\nkissmetrics.com\r\nchartbeat.com\r\nparsely.com\r\nomniconvert.com\r\n\r\n# ========================================\r\n# 📢 Advertising & Marketing\r\n# ========================================\r\ngooglesyndication.com\r\ngoogleadservices.com\r\nadservice.google.com\r\nadnxs.com\r\nads.yahoo.com\r\nadvertising.com\r\nadsystem.com\r\nadroll.com\r\ncriteo.com\r\ncriteo.net\r\noutbrain.com\r\ntaboola.com\r\nmedia.net\r\nyieldmo.com\r\npubmatic.com\r\nrubiconproject.com\r\nopenx.net\r\nindexww.com\r\ncontextweb.com\r\nturn.com\r\nadform.net\r\nsmartadserver.com\r\nserving-sys.com\r\nadsafeprotected.com\r\ndoubleverify.com\r\nmoatads.com\r\n\r\n# ========================================\r\n# 👥 Social Media Tracking\r\n# ========================================\r\nfacebook.com/plugins\r\ntwitter.com/widgets\r\nplatform.twitter.com\r\nlinkedin.com/widgets\r\nplatform.linkedin.com\r\ninstagram.com/embed\r\npinterest.com/widgets\r\nsnapchat.com/widgets\r\ntiktok.com/embed\r\nreddit.com/static\r\n\r\n# ========================================\r\n# 🖼️ Image Formats (Saves a lot!)\r\n# ========================================\r\n.jpg\r\n.jpeg\r\n.png\r\n.gif\r\n.webp\r\n.ico\r\n.svg\r\n.bmp\r\n.tiff\r\n.avif\r\n.jfif\r\n\r\n# ========================================\r\n# 🎨 Fonts & CSS\r\n# ========================================\r\n.woff\r\n.woff2\r\n.ttf\r\n.eot\r\n.otf\r\n.css\r\nfonts.googleapis.com\r\nfonts.gstatic.com\r\nuse.typekit.net\r\ncloud.typography.com\r\n\r\n# ========================================\r\n# 📦 JavaScript & Maps\r\n# ========================================\r\n.js\r\n.map\r\n.min.js\r\n.bundle.js\r\n\r\n# ========================================\r\n# 🎬 Media Files (Video/Audio)\r\n# ========================================\r\n.mp4\r\n.webm\r\n.ogg\r\n.mp3\r\n.wav\r\n.m4a\r\n.flac\r\n.aac\r\n\r\n# ========================================\r\n# 📄 Documents & Archives\r\n# ========================================\r\n.pdf\r\n.zip\r\n.rar\r\n.7z\r\n.tar\r\n.gz\r\n.doc\r\n.docx\r\n.xls\r\n.xlsx\r\n.ppt\r\n.pptx\r\n\r\n# ========================================\r\n# 🎯 Common Tracking Paths\r\n# ========================================\r\n/pixel\r\n/pixel.gif\r\n/track\r\n/tracking\r\n/analytics\r\n/beacon\r\n/log\r\n/collect\r\n/event\r\n/events\r\n/telemetry\r\n/metrics\r\n/stats\r\n/ping\r\n/heartbeat\r\n/_ga\r\n/_gat\r\n/_gid\r\n/gtag\r\n/fbevents\r\n/tr.gif\r\n/impression\r\n/view\r\n/click\r\n\r\n# ========================================\r\n# 🌐 CDN & Static Assets\r\n# ========================================\r\ncdnjs.cloudflare.com/ajax/libs\r\ncdn.jsdelivr.net\r\nunpkg.com\r\nstackpath.bootstrapcdn.com\r\nmaxcdn.bootstrapcdn.com\r\najax.googleapis.com/ajax/libs\r\n\r\n# ========================================\r\n# 🔌 Third-Party Widgets\r\n# ========================================\r\ndisqus.com\r\nzopim.com\r\nlivechatinc.com\r\ntawk.to\r\nintercom.io\r\ndrift.com\r\nolark.com\r\nzendesk.com/embeddable\r\nhelpscout.com\r\nuservoice.com\r\n\r\n# ========================================\r\n# 🎮 Gaming/App Analytics\r\n# ========================================\r\nunity3d.com/stats\r\nunityads.unity3d.com\r\napi.gameanalytics.com\r\nfirebase.google.com\r\n\r\n# ========================================\r\n# 🛡️ Security/Bot Detection (Careful!)\r\n# ========================================\r\n# Note: Blocking these may break some websites\r\n# recaptcha.net\r\n# google.com/recaptcha\r\n# hcaptcha.com\r\n# cloudflare.com/cdn-cgi\r\n\r\n# ========================================\r\n# 🎯 Specific Trackers\r\n# ========================================\r\nbat.bing.com\r\nt.co\r\nbit.ly\r\now.ly\r\ngoo.gl\r\ntinyurl.com\r\n\r\n# ========================================\r\n# 📱 Mobile Analytics\r\n# ========================================\r\napp-measurement.com\r\nfirebaselogging.googleapis.com\r\ncrashlytics.com\r\nfabric.io\r\n\r\n# ========================================\r\n# 💬 Chat Widgets\r\n# ========================================\r\n/chat.js\r\n/livechat\r\n/widget\r\n/embed.js\r\n\r\n# ========================================\r\n# ⚙️ Add your own patterns here:\r\n# ========================================\r\n# Examples:\r\n# example-tracker.com\r\n# .mp4\r\n# /my-custom-tracking-path\r\n\r\n"
  },
  {
    "path": "build.bat",
    "content": "@echo off\r\nREM Build script for FlareTunnel Go version (Windows)\r\n\r\necho 🔨 Building FlareTunnel...\r\necho.\r\n\r\necho 📦 Downloading dependencies...\r\ngo mod download\r\nif errorlevel 1 (\r\n    echo ❌ Failed to download dependencies\r\n    pause\r\n    exit /b 1\r\n)\r\n\r\necho.\r\necho 🏗️  Building for Windows...\r\ngo build -ldflags=\"-s -w\" -o flaretunnel.exe flaretunnel.go\r\nif errorlevel 1 (\r\n    echo ❌ Build failed\r\n    pause\r\n    exit /b 1\r\n)\r\n\r\necho.\r\necho ✅ Build complete: flaretunnel.exe\r\necho.\r\necho 🚀 Quick start:\r\necho   flaretunnel.exe config              # Configure accounts\r\necho   flaretunnel.exe create --count 5    # Create workers\r\necho   flaretunnel.exe list --verbose      # List workers\r\necho   flaretunnel.exe tunnel --verbose    # Start proxy\r\necho.\r\n\r\nset /p REPLY=\"Build for other platforms? (y/N) \"\r\nif /i \"%REPLY%\"==\"y\" (\r\n    echo.\r\n    echo 🌍 Building for multiple platforms...\r\n    \r\n    echo   Building for Linux (amd64)...\r\n    set GOOS=linux\r\n    set GOARCH=amd64\r\n    go build -ldflags=\"-s -w\" -o flaretunnel-linux-amd64 flaretunnel.go\r\n    \r\n    echo   Building for macOS (amd64)...\r\n    set GOOS=darwin\r\n    set GOARCH=amd64\r\n    go build -ldflags=\"-s -w\" -o flaretunnel-macos-amd64 flaretunnel.go\r\n    \r\n    echo   Building for macOS (arm64)...\r\n    set GOOS=darwin\r\n    set GOARCH=arm64\r\n    go build -ldflags=\"-s -w\" -o flaretunnel-macos-arm64 flaretunnel.go\r\n    \r\n    echo.\r\n    echo ✅ Cross-compilation complete!\r\n    echo.\r\n    echo 📦 Built binaries:\r\n    dir /b flaretunnel-*\r\n    echo.\r\n)\r\n\r\necho ✨ Done!\r\npause\r\n\r\n"
  },
  {
    "path": "build.sh",
    "content": "#!/bin/bash\n# Build script for FlareTunnel Go version\n\nset -e\n\necho \"🔨 Building FlareTunnel...\"\n\n# Get dependencies\necho \"📦 Downloading dependencies...\"\ngo mod download\n\n# Build for current platform\necho \"🏗️  Building for current platform...\"\ngo build -ldflags=\"-s -w\" -o flaretunnel flaretunnel.go\n\necho \"✅ Build complete: ./flaretunnel\"\necho \"\"\necho \"📊 Binary size:\"\nls -lh flaretunnel | awk '{print $5}'\necho \"\"\necho \"🚀 Quick start:\"\necho \"  ./flaretunnel config          # Configure accounts\"\necho \"  ./flaretunnel create --count 5   # Create workers\"\necho \"  ./flaretunnel list --verbose     # List workers\"\necho \"  ./flaretunnel tunnel --verbose   # Start proxy\"\necho \"\"\n\n# Optional: Build for other platforms\nread -p \"Build for other platforms? (y/N) \" -n 1 -r\necho\nif [[ $REPLY =~ ^[Yy]$ ]]\nthen\n    echo \"🌍 Building for multiple platforms...\"\n    \n    # Windows\n    echo \"  Building for Windows (amd64)...\"\n    GOOS=windows GOARCH=amd64 go build -ldflags=\"-s -w\" -o flaretunnel-windows-amd64.exe flaretunnel.go\n    \n    # Linux\n    echo \"  Building for Linux (amd64)...\"\n    GOOS=linux GOARCH=amd64 go build -ldflags=\"-s -w\" -o flaretunnel-linux-amd64 flaretunnel.go\n    \n    # macOS Intel\n    echo \"  Building for macOS (amd64)...\"\n    GOOS=darwin GOARCH=amd64 go build -ldflags=\"-s -w\" -o flaretunnel-macos-amd64 flaretunnel.go\n    \n    # macOS Apple Silicon\n    echo \"  Building for macOS (arm64)...\"\n    GOOS=darwin GOARCH=arm64 go build -ldflags=\"-s -w\" -o flaretunnel-macos-arm64 flaretunnel.go\n    \n    # Linux ARM (Raspberry Pi, etc.)\n    echo \"  Building for Linux (arm64)...\"\n    GOOS=linux GOARCH=arm64 go build -ldflags=\"-s -w\" -o flaretunnel-linux-arm64 flaretunnel.go\n    \n    echo \"\"\n    echo \"✅ Cross-compilation complete!\"\n    echo \"\"\n    echo \"📦 Built binaries:\"\n    ls -lh flaretunnel-* | awk '{print \"  \" $9 \" - \" $5}'\n    echo \"\"\nfi\n\necho \"✨ Done!\"\n\n"
  },
  {
    "path": "example_usage.py",
    "content": "#!/usr/bin/env python3\r\n\"\"\"\r\nFlareTunnel - Quick Usage Example\r\nMake sure proxy is running: python FlareTunnel.py tunnel --verbose\r\n\"\"\"\r\n\r\nimport requests\r\nimport urllib3\r\nimport time\r\n\r\n# Disable SSL warnings\r\nurllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)\r\n\r\n# FlareTunnel proxy configuration\r\nproxies = {\r\n    'http': 'http://127.0.0.1:8080',\r\n    'https': 'http://127.0.0.1:8080'\r\n}\r\n\r\nprint(\"=\" * 80)\r\nprint(\"FlareTunnel - Usage Example\")\r\nprint(\"=\" * 80)\r\nprint()\r\nprint(\"Make sure proxy is running: python FlareTunnel.py tunnel --verbose\")\r\nprint()\r\nprint(\"=\" * 80)\r\nprint()\r\n\r\n# Test 1: Simple GET\r\nprint(\"📡 Test 1: GET Request\")\r\nprint(\"-\" * 40)\r\ntry:\r\n    response = requests.get(\r\n        \"https://httpbin.org/get?test=123\",\r\n        proxies=proxies,\r\n        verify=False,\r\n        timeout=10\r\n    )\r\n    print(f\"✓ Status: {response.status_code}\")\r\n    data = response.json()\r\n    print(f\"✓ Origin IP: {data.get('origin', 'N/A')}\")\r\n    print(f\"✓ Args: {data.get('args', {})}\")\r\n    print()\r\nexcept Exception as e:\r\n    print(f\"✗ Error: {e}\")\r\n    print()\r\n\r\n# Test 2: POST with JSON\r\nprint(\"📤 Test 2: POST Request with JSON\")\r\nprint(\"-\" * 40)\r\ntry:\r\n    response = requests.post(\r\n        \"https://httpbin.org/post\",\r\n        json={\r\n            \"username\": \"testuser\",\r\n            \"action\": \"login\",\r\n            \"timestamp\": time.time()\r\n        },\r\n        proxies=proxies,\r\n        verify=False,\r\n        timeout=10\r\n    )\r\n    print(f\"✓ Status: {response.status_code}\")\r\n    data = response.json()\r\n    print(f\"✓ Received JSON: {data.get('json', {})}\")\r\n    print()\r\nexcept Exception as e:\r\n    print(f\"✗ Error: {e}\")\r\n    print()\r\n\r\n# Test 3: Custom Headers\r\nprint(\"📋 Test 3: Custom Headers\")\r\nprint(\"-\" * 40)\r\ntry:\r\n    response = requests.get(\r\n        \"https://httpbin.org/headers\",\r\n        headers={\r\n            \"X-Custom-Header\": \"FlareTunnel-Test\",\r\n            \"User-Agent\": \"FlareTunnel/2.0\"\r\n        },\r\n        proxies=proxies,\r\n        verify=False,\r\n        timeout=10\r\n    )\r\n    print(f\"✓ Status: {response.status_code}\")\r\n    data = response.json()\r\n    headers_received = data.get('headers', {})\r\n    print(f\"✓ Custom Header: {headers_received.get('X-Custom-Header', 'Not received')}\")\r\n    print(f\"✓ User-Agent: {headers_received.get('User-Agent', 'Not received')}\")\r\n    print()\r\nexcept Exception as e:\r\n    print(f\"✗ Error: {e}\")\r\n    print()\r\n\r\n# Test 4: IP Rotation Check\r\nprint(\"🔄 Test 4: IP Rotation (5 requests)\")\r\nprint(\"-\" * 40)\r\nips = set()\r\nfor i in range(5):\r\n    try:\r\n        response = requests.get(\r\n            \"https://httpbin.org/ip\",\r\n            proxies=proxies,\r\n            verify=False,\r\n            timeout=10\r\n        )\r\n        if response.status_code == 200:\r\n            ip = response.json().get('origin', 'N/A')\r\n            ips.add(ip)\r\n            print(f\"  Request {i+1}: {ip}\")\r\n        time.sleep(0.5)\r\n    except Exception as e:\r\n        print(f\"  Request {i+1}: Error - {e}\")\r\n\r\nprint(f\"\\n✓ Unique IPs: {len(ips)}\")\r\nfor ip in sorted(ips):\r\n    print(f\"    - {ip}\")\r\nprint()\r\n\r\n# Test 5: Real Website\r\nprint(\"🌐 Test 5: Real Website (example.com)\")\r\nprint(\"-\" * 40)\r\ntry:\r\n    response = requests.get(\r\n        \"https://example.com\",\r\n        proxies=proxies,\r\n        verify=False,\r\n        timeout=10\r\n    )\r\n    print(f\"✓ Status: {response.status_code}\")\r\n    print(f\"✓ Size: {len(response.content):,} bytes\")\r\n    # Show first 100 chars\r\n    content_preview = response.text[:100].replace('\\n', ' ')\r\n    print(f\"✓ Preview: {content_preview}...\")\r\n    print()\r\nexcept Exception as e:\r\n    print(f\"✗ Error: {e}\")\r\n    print()\r\n\r\n# Summary\r\nprint(\"=\" * 80)\r\nprint(\"✅ All tests completed!\")\r\nprint(\"=\" * 80)\r\nprint()\r\nprint(\"💡 Key Points:\")\r\nprint(\"  • All requests went through Cloudflare Workers\")\r\nprint(\"  • Use rotation modes to change IPs (--mode random)\")\r\nprint(\"  • Use blacklist to save Worker requests (default: blacklist-minimal.txt)\")\r\nprint(\"  • IP addresses are blocked by default (Cloudflare doesn't support them)\")\r\nprint()\r\nprint(\"📚 Learn More:\")\r\nprint(\"  • README.md - Full documentation\")\r\nprint(\"  • python FlareTunnel.py --help - All commands\")\r\nprint()\r\n"
  },
  {
    "path": "go.mod",
    "content": "module flaretunnel\n\ngo 1.21\n\nrequire github.com/olekukonko/tablewriter v0.0.5\n\nrequire github.com/mattn/go-runewidth v0.0.9 // indirect\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=\ngithub.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=\ngithub.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=\ngithub.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=\n"
  }
]