MCPcopy Index your code
hub / github.com/Danny-Dasilva/CycleTLS

github.com/Danny-Dasilva/CycleTLS @main

Chat with this repo
repository ↗ · DeepWiki ↗ · + Follow
450 symbols 1,608 edges 101 files 187 documented · 42% 1 cross-repo links
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

CycleTLS

<img src="https://github.com/Danny-Dasilva/CycleTLS/raw/main/docs/media/Banner.png" alt="CycleTLS"/>

Accepting Community Support and PR's

build GoDoc license Go Report Card npm version chat on Discord

If you have a API change or feature request feel free to open an Issue

🚀 Features

  • High-performance Built-in goroutine pool used for handling asynchronous requests
  • Custom header ordering via fhttp
  • Proxy support | Socks4, Socks5, Socks5h
  • Ja3 Token configuration
  • HTTP/3 and QUIC support
  • WebSocket client
  • Server-Sent Events (SSE)
  • Connection reuse
  • JA4 fingerprinting

Table of contents

Dependencies

node ^v18.0
golang ^v1.21x

Installation

Node Js

$ npm install cycletls

Golang

$ go get github.com/Danny-Dasilva/CycleTLS/cycletls 

Usage

Example CycleTLS Request for Typescript and Javascript

You can run this test in tests/simple.test.ts

const initCycleTLS = require('cycletls');
// Typescript: import initCycleTLS from 'cycletls';

(async () => {
  // Initiate CycleTLS
  const cycleTLS = await initCycleTLS();

  // Send request
  const response = await cycleTLS('https://ja3er.com/json', {
    body: '',
    ja3: '771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0',
    userAgent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0',
    proxy: 'http://username:password@hostname.com:443'
  }, 'get');

  // Parse response as JSON
  const data = await response.json();
  console.log(data);

  // Cleanly exit CycleTLS
  await cycleTLS.exit();

})();

JA4R (Raw) TLS Fingerprinting

Important: Pass ja4r to configure the TLS ClientHello. JA4 (hash) is a report-only value; configuring with a JA4 hash will not change your fingerprint.

JA4R is the raw format of JA4 fingerprinting that allows explicit configuration of cipher suites, extensions, and signature algorithms:

JavaScript Example

const initCycleTLS = require('cycletls');

(async () => {
  const cycleTLS = await initCycleTLS();

  // Chrome JA4R fingerprint (raw format)
  const response = await cycleTLS('https://tls.peet.ws/api/all', {
    ja4r: 't13d1516h2_002f,0035,009c,009d,1301,1302,1303,c013,c014,c02b,c02c,c02f,c030,cca8,cca9_0000,0005,000a,000b,000d,0012,0017,001b,0023,002b,002d,0033,44cd,fe0d,ff01_0403,0804,0401,0503,0805,0501,0806,0601'
  });

  const data = await response.json();
  console.log('JA4:', data.tls.ja4);
  console.log('JA4_r:', data.tls.ja4_r);
  console.log('TLS Version:', data.tls.tls_version_negotiated);

  await cycleTLS.exit();
})();

Golang JA4R Example

package main

import (
    "log"
    "github.com/Danny-Dasilva/CycleTLS/cycletls"
)

func main() {
    client := cycletls.Init(cycletls.WithRawBytes())
    defer client.Close()

    // Chrome JA4R fingerprint (raw format)
    response, err := client.Do("https://tls.peet.ws/api/all", cycletls.Options{
        Ja4r: "t13d1516h2_002f,0035,009c,009d,1301,1302,1303,c013,c014,c02b,c02c,c02f,c030,cca8,cca9_0000,0005,000a,000b,000d,0012,0017,001b,0023,002b,002d,0033,44cd,fe0d,ff01_0403,0804,0401,0503,0805,0501,0806,0601",
        UserAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
    }, "GET")

    if err != nil {
        log.Fatal(err)
    }
    log.Println("Response with JA4R:", response.Status)
}

HTTP/2 Fingerprinting

HTTP/2 fingerprinting allows you to mimic specific browser HTTP/2 implementations:

JavaScript Example

const initCycleTLS = require('cycletls');

(async () => {
  const cycleTLS = await initCycleTLS();

  // Firefox HTTP/2 fingerprint
  const response = await cycleTLS('https://tls.peet.ws/api/all', {
    http2Fingerprint: '1:65536;2:0;4:131072;5:16384|12517377|0|m,p,a,s',
    ja3: '771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0',
    userAgent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:141.0) Gecko/20100101 Firefox/141.0'
  });

  const data = await response.json();
  console.log('HTTP/2 Fingerprint:', data.http2.akamai_fingerprint);
  console.log('Settings:', data.http2.sent_frames[0].settings);

  await cycleTLS.exit();
})();

Golang HTTP/2 Example

package main

import (
    "log"
    "github.com/Danny-Dasilva/CycleTLS/cycletls"
)

func main() {
    client := cycletls.Init()
    defer client.Close()

    // Firefox HTTP/2 fingerprint
    response, err := client.Do("https://tls.peet.ws/api/all", cycletls.Options{
        HTTP2Fingerprint: "1:65536;2:0;4:131072;5:16384|12517377|0|m,p,a,s",
        UserAgent: "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:141.0) Gecko/20100101 Firefox/141.0",
    }, "GET")

    if err != nil {
        log.Fatal(err)
    }
    log.Println("Response with HTTP/2 fingerprint:", response.Status)
}

Common Browser HTTP/2 Fingerprints

Browser HTTP/2 Fingerprint Description
Firefox 1:65536;2:0;4:131072;5:16384\|12517377\|0\|m,p,a,s Smaller window size, MPAS priority
Chrome 1:65536;2:0;4:6291456;6:262144\|15663105\|0\|m,a,s,p Larger window size, MASP priority

Combined Fingerprinting Example

const initCycleTLS = require('cycletls');

(async () => {
  const cycleTLS = await initCycleTLS();

  // Complete Chrome browser fingerprint with JA4R
  const response = await cycleTLS('https://tls.peet.ws/api/all', {
    ja4r: 't13d1516h2_002f,0035,009c,009d,1301,1302,1303,c013,c014,c02b,c02c,c02f,c030,cca8,cca9_0000,0005,000a,000b,000d,0012,0017,001b,0023,002b,002d,0033,44cd,fe0d,ff01_0403,0804,0401,0503,0805,0501,0806,0601',
    http2Fingerprint: '1:65536;2:0;4:131072;5:16384|12517377|0|m,p,a,s',
    userAgent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:141.0) Gecko/20100101 Firefox/141.0'
  });

  const data = await response.json();
  console.log('Complete fingerprint applied successfully');
  console.log('JA4:', data.tls.ja4);
  console.log('HTTP/2:', data.http2.akamai_fingerprint);

  await cycleTLS.exit();
})();

Streaming Responses (Axios-style)

CycleTLS supports axios-compatible streaming responses for real-time data processing:

Basic Streaming Example

const initCycleTLS = require('cycletls');

(async () => {
  const cycleTLS = await initCycleTLS();

  // Get streaming response
  const response = await cycleTLS.get('https://httpbin.org/stream/3', {
    headers: { Authorization: `Bearer your_token_here` },
    responseType: 'stream',
    ja3: '771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0',
    userAgent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0'
  });

  const stream = response.data;

  stream.on('data', data => {
    console.log('Received chunk:', data.toString());
  });

  stream.on('end', () => {
    console.log("stream done");
    await cycleTLS.exit();
  });

  stream.on('error', (error) => {
    console.error('Stream error:', error);
    await cycleTLS.exit();
  });
})();

Advanced Streaming with Error Handling

const initCycleTLS = require('cycletls');

(async () => {
  const cycleTLS = await initCycleTLS();

  try {
    const response = await cycleTLS.get('https://httpbin.org/drip?numbytes=100&duration=2', {
      responseType: 'stream',
      ja3: '771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0',
      userAgent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0',
    });

    console.log('Status:', response.status);
    console.log('Headers:', response.headers);

    const chunks = [];

    response.data.on('data', (chunk) => {
      chunks.push(chunk);
      console.log(`Received ${chunk.length} bytes`);
    });

    response.data.on('end', () => {
      console.log('Stream complete');
      const fullData = Buffer.concat(chunks);
      console.log('Total received:', fullData.length, 'bytes');
      await cycleTLS.exit();
    });

    response.data.on('error', (error) => {
      console.error('Stream error:', error);
      await cycleTLS.exit();
    });

  } catch (error) {
    console.error('Request failed:', error);
    await cycleTLS.exit();
  }
})();

Non-Streaming Responses (Default Behavior)

For non-streaming responses, CycleTLS works exactly as before:

// These return buffered responses (existing behavior)
const jsonResponse = await cycleTLS.get('https://httpbin.org/json', {
  responseType: 'json' // or omit for default JSON parsing
});
const jsonData = await jsonResponse.json();
console.log(jsonData); // Parsed JSON object

const textResponse = await cycleTLS.get('https://httpbin.org/html', {
  responseType: 'text'
});
const textData = await textResponse.text();
console.log(textData); // String content

Example CycleTLS Request for Golang

package main

import (
    "log"
    "github.com/Danny-Dasilva/CycleTLS/cycletls"
)

func main() {
    client := cycletls.Init()
    defer client.Close()

    response, err := client.Do("https://ja3er.com/json", cycletls.Options{
        Body: "",
        Ja3: "771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0",
        UserAgent: "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0",
        EnableConnectionReuse: true, // Enable connection reuse for better performance
    }, "GET")
    if err != nil {
        log.Print("Request Failed: " + err.Error())
    }
    log.Println(response)
}

Example using your own custom http.Client

import (
    "github.com/Danny-Dasilva/CycleTLS/cycletls"
    http "github.com/Danny-Dasilva/fhttp" // note this is a drop-in replacement for net/http
)

func main() {
    ja3 := "771,52393-52392-52244-52243-49195-49199-49196-49200-49171-49172-156-157-47-53-10,65281-0-23-35-13-5-18-16-30032-11-10,29-23-24,0"
    ua := "Chrome Version 57.0.2987.110 (64-bit) Linux"

     cycleClient := &http.Client{
        Transport:     cycletls.NewTransport(ja3, ua),
     }

    resp, err := cycleClient.Get("https://tls.peet.ws/")
    ...
}

Performance Enhancement: Raw Bytes Option

The default Init() method provides the standard v1 API with chan Response. For performance-critical applications that can handle raw bytes, use the WithRawBytes() option:

package main

import (
    "encoding/json"
    "fmt"
    "github.com/Danny-Dasilva/CycleTLS/cycletls"
)

func main() {
    // Use WithRawBytes() option for performance enhancement
    client := cycletls.Init(cycletls.WithRawBytes())
    defer client.Close()

    // Queue a request
    go func() {
        client.Queue("https://ja3er.com/json", cycletls.Options{
            Ja3: "771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0",
            UserAgent: "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0",
        }, "GET")
    }()

    // Performance pattern: receive raw bytes from RespChanV2
    select {
    case responseBytes := <-client.RespChanV2:
        var response cycletls.Response
        json.Unmarshal(responseBytes, &response)
        fmt.Printf("Status: %d\n", response.Status)
        fmt.Printf("Body: %s\n", response.Body)
    // Alternative: still supports v1 pattern via RespChan
    case response := <-client.RespChan:
        fmt.Printf("Status: %d\n", response.Status)
        fmt.Printf("Body: %s\n", response.Body)
    }
}

Note: Use Init() for standard compatibility with chan Response. Use Init(cycletls.WithRawBytes()) when you need the performance benefits of handling raw []byte responses directly.

Creating an instance

In order to create a cycleTLS instance, you can run the following:

JavaScript

// The initCycleTLS function spawns a Golang process that handles all requests concurrently via goroutine loops. 
const initCycleTLS = require('cycletls');
// import initCycleTLS from 'cycletls';

// Async/Await method
const cycleTLS = await initCycleTLS();
// With optional configuration
const cycleTLS = await initCycleTLS({ port: 9118, timeout: 30000 });
// .then method
initCycleTLS().then((cycleTLS) => {});

Golang

import (
    "github.com/Danny-Dasilva/CycleTLS/cycletls"
)

//The `Init` function initializes golang channels to process requests. 
client := cycletls.Init()

CycleTLS Alias Methods

The following methods exist in CycleTLS

**cyc

Extension points exported contracts — how you extend this code

PreserveIDExtension (Interface)
PreserveIDExtension is an interface for extensions that preserve their original extension ID [7 implementers]
cycletls/extensions.go
Option (FuncType)
Option configures a CycleTLS client
cycletls/index.go
CycleTLSClient (Interface)
(no doc) [1 implementers]
src/index.ts
CycleTLSOptions (Interface)
(no doc)
tests/test-utils.ts
Request (Interface)
(no doc)
tests/integration.test.ts
Request (Interface)
(no doc)
tests/encoding.test.ts
Cookie (Interface)
(no doc)
src/index.ts
TimeoutOptions (Interface)
(no doc)
src/index.ts

Core symbols most depended-on inside this repo

Error
called by 89
cycletls/errors.go
Close
called by 88
cycletls/index.go
Do
called by 80
cycletls/index.go
Init
called by 53
cycletls/index.go
json
called by 42
src/index.ts
Write
called by 37
cycletls/connect.go
exit
called by 36
src/index.ts
initCycleTLS
called by 36
src/index.ts

Shape

Function 236
Method 126
Struct 65
Interface 13
Class 8
FuncType 1
TypeAlias 1

Languages

Go72%
TypeScript28%

Modules by API surface

src/index.ts100 symbols
cycletls/utils.go29 symbols
cycletls/index.go27 symbols
cycletls/extensions.go25 symbols
cycletls/client.go17 symbols
cycletls/connect.go16 symbols
cycletls/http3.go15 symbols
cycletls/roundtripper.go12 symbols
tests/golang_hold/index copy.go10 symbols
cycletls/tests/integration/binary_data_test.go10 symbols
tests/golang_hold/working.go9 symbols
tests/golang_hold/weird_dual.go9 symbols

Used by 1 indexed graphs manifest dependencies, hub-wide

For agents

$ claude mcp add CycleTLS \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact