<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ebrahim Shafiei</title>
    <description>The latest articles on DEV Community by Ebrahim Shafiei (@ebrasha).</description>
    <link>https://dev.to/ebrasha</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3976161%2F64b916eb-0d72-444a-91d3-9e4dcc8e6805.png</url>
      <title>DEV Community: Ebrahim Shafiei</title>
      <link>https://dev.to/ebrasha</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ebrasha"/>
    <language>en</language>
    <item>
      <title>One Binary to Manage an SSH Tunnel Server: Abdal 4iProto Panel</title>
      <dc:creator>Ebrahim Shafiei</dc:creator>
      <pubDate>Tue, 09 Jun 2026 22:49:40 +0000</pubDate>
      <link>https://dev.to/ebrasha/one-binary-to-manage-an-ssh-tunnel-server-abdal-4iproto-panel-1ndm</link>
      <guid>https://dev.to/ebrasha/one-binary-to-manage-an-ssh-tunnel-server-abdal-4iproto-panel-1ndm</guid>
      <description>&lt;h2&gt;
  
  
  Why a single binary matters for tunnel administration
&lt;/h2&gt;

&lt;p&gt;If you have ever stood up an SSH tunnel server on a fresh VPS, you know the real cost is not the server—it is everything around it. You need a way to add and revoke users, cap their bandwidth, watch sessions in real time, block abusive IPs, and survive a reboot without a 12-step runbook. The usual answer is a stack: a runtime, a web framework, a database, a reverse proxy, a process manager. Each layer is one more thing that breaks at 2 a.m.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Abdal 4iProto Panel&lt;/strong&gt; takes the opposite bet. It is a web-based management interface for the &lt;a href="https://github.com/ebrasha/abdal-4iproto-server" rel="noopener noreferrer"&gt;Abdal 4iProto Server&lt;/a&gt;—a dedicated SSH tunnel server—written in &lt;strong&gt;Go&lt;/strong&gt; and compiled to a &lt;strong&gt;single executable with embedded resources&lt;/strong&gt;. CSS, JS, HTML templates, and translation files all live inside the binary. There is no runtime to install, no external dependency to pin, no asset directory to ship. You drop one file on Windows or Linux, optionally register it as a service, and you have a full admin console. This article walks through how it is built and how an expert operator would actually use it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🔗 &lt;strong&gt;Panel repo:&lt;/strong&gt; &lt;a href="https://github.com/ebrasha/abdal-4iproto-panel" rel="noopener noreferrer"&gt;https://github.com/ebrasha/abdal-4iproto-panel&lt;/a&gt;&lt;br&gt;
🔗 &lt;strong&gt;Server repo:&lt;/strong&gt; &lt;a href="https://github.com/ebrasha/abdal-4iproto-server" rel="noopener noreferrer"&gt;https://github.com/ebrasha/abdal-4iproto-server&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The architecture in one sentence
&lt;/h2&gt;

&lt;p&gt;The panel is a Go process that embeds its own web assets, persists configuration as flat JSON files, talks to the 4iProto SSH tunnel server it manages, and can run as a Windows Service or a Linux &lt;code&gt;systemd&lt;/code&gt; unit. That is the whole shape of it—and the simplicity is the point.&lt;/p&gt;

&lt;p&gt;Configuration lives in plain JSON, not a database engine. On first run the panel writes &lt;code&gt;abdal-4iproto-panel.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;52202&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ebrasha"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ebrasha1309"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"logging"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"blocked_ips"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"max_login_attempts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"login_attempt_window"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"block_duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a tunnel management surface, JSON state is a defensible choice: the data set (users, blocked IPs, server config) is small, human-auditable, trivially backed up with &lt;code&gt;cp&lt;/code&gt;, and never introduces a separate failure domain. You can inspect or diff the live state with tools you already have. Note the SSH-native footprint of the install set, too—&lt;code&gt;id_ed25519&lt;/code&gt; and &lt;code&gt;id_ed25519.pub&lt;/code&gt; ship alongside the binaries, since the server is doing real SSH tunneling.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Change &lt;code&gt;ebrasha1309&lt;/code&gt; immediately after first login. Defaults are for first boot, not production.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The administrative workflow
&lt;/h2&gt;

&lt;p&gt;Point a browser at &lt;code&gt;http://localhost:52202&lt;/code&gt; (or your configured port), authenticate, and you land on a dashboard summarizing users, sessions, and traffic. From there the work is organized around the lifecycle of a tunnel deployment rather than a flat settings dump.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User provisioning&lt;/strong&gt; is full CRUD, but the interesting part is per-user policy. Each account carries far more than a username and password: a role (&lt;code&gt;admin&lt;/code&gt;/&lt;code&gt;user&lt;/code&gt;), blocked domains and IPs scoped to that user, concurrent session limits and TTL, a speed cap in KB/s, a traffic quota in MB, and individual logging preferences. This is enough to run differentiated tiers—a throttled trial account and an uncapped admin account—without touching the server binary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server configuration&lt;/strong&gt; is edited from the same UI: listening ports (multiple are supported), the default shell for Windows/Linux targets, the maximum authentication attempts, and even the server's advertised version signature. The panel writes these to the server's config and the change takes effect on the managed service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security&lt;/strong&gt; is handled at two layers. There is straightforward IP blocking, and there is an automatic brute-force defense with a configurable attempt ceiling, a sliding time window, and a block duration. Cross the threshold inside the window and the offending IP is blocked automatically for the configured period—no manual intervention.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monitoring&lt;/strong&gt; is where an operator spends real time. User access logs update over Ajax without a full page reload. Traffic monitoring tracks consumption per user—session-based and cumulative—and surfaces warnings as accounts approach their quota. The sessions view reflects live state from the server: session ID, username, source IP, client version, creation time, and last-seen timestamp, with the ability to revoke a session.&lt;/p&gt;

&lt;p&gt;The whole console is bilingual (English and Persian), responsive down to a mobile hamburger menu, and—because configuration changes can require a clean reload—the panel &lt;strong&gt;auto-restarts its own service&lt;/strong&gt; after you save, so applied settings are never half-live.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Telegram bot: a study in isolation
&lt;/h2&gt;

&lt;p&gt;The feature most worth dissecting is the optional Telegram bot, which mirrors every administrative action from the web UI. Enabling it is a config flow: create a bot via &lt;code&gt;@BotFather&lt;/code&gt;, grab your numeric ID from &lt;code&gt;@userinfobot&lt;/code&gt;, then in &lt;strong&gt;Panel Configuration → 🤖 Telegram Bot&lt;/strong&gt; tick enable, paste the token, and list admin IDs. Settings persist under a &lt;code&gt;telegram_bot&lt;/code&gt; key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"telegram_bot"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123456789:AA...your-bot-token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"admins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;111111111&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;222222222&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The security model is deliberately blunt: the bot drops every update that does not originate from an admin Telegram ID and replies with a short "not authorized" message, so an attacker cannot silently probe behavior.&lt;/p&gt;

&lt;p&gt;What makes the bot worth studying is its concurrency design. It is not a side thread bolted onto the request handler—it is a &lt;strong&gt;fully isolated subsystem&lt;/strong&gt; inside the panel process, with its own HTTP connection pool, dispatcher worker pool, asynchronous log channel, and a debounced restart queue. The explicit goal: a busy panel UI or slow disk I/O can never slow the bot down. The update pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────┐      ┌──────────────────────┐
│ Telegram getUpdates     │ ───► │ updates channel      │
│ (1 goroutine)           │      │ (cap 1024)           │
└─────────────────────────┘      └──────────┬───────────┘
                                            │
                          ┌─────────────────┼─────────────────┐
                          ▼                 ▼                 ▼
                   ┌──────────┐      ┌──────────┐      ┌──────────┐
                   │ worker 1 │ ...  │ worker N │      │ worker N │ (NumCPU*2, ≥8)
                   └────┬─────┘      └────┬─────┘      └────┬─────┘
                        │                 │                 │
                        ▼ (go r(ctx,b,u)) ▼                 ▼
                   ┌────────────────────────────────────────────┐
                   │ Per-handler goroutine                       │
                   │  → trackerMiddleware (WaitGroup +1)         │
                   │  → adminOnly                                │
                   │  → recover                                  │
                   │  → handler (sendText via HTTP/2 conn pool)  │
                   └────────────────────────────────────────────┘
                        │
                        ▼ (svc.Info/Warning/Error)
                   ┌────────────────┐
                   │ async log chan │ ──► dedicated writer ──► panelLogger
                   │ (cap 1024)     │
                   └────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A single goroutine long-polls &lt;code&gt;getUpdates&lt;/code&gt; and pushes into a buffered channel (capacity 1024). A worker pool sized at &lt;code&gt;NumCPU*2&lt;/code&gt; (floor of 8) drains it. Each update spawns a per-handler goroutine wrapped in a middleware chain: a &lt;code&gt;trackerMiddleware&lt;/code&gt; that increments a &lt;code&gt;WaitGroup&lt;/code&gt; for clean shutdown, an &lt;code&gt;adminOnly&lt;/code&gt; gate, a &lt;code&gt;recover&lt;/code&gt; so one panicking handler never takes down the pool, and finally the handler itself sending replies through the shared HTTP/2 connection pool. Logging is fire-and-forget into another 1024-cap channel drained by a dedicated writer—so logging never blocks a handler. This is textbook Go: bounded channels for backpressure, a fixed worker pool to cap concurrency, &lt;code&gt;recover&lt;/code&gt; per goroutine for fault isolation, and a &lt;code&gt;WaitGroup&lt;/code&gt; for graceful drain.&lt;/p&gt;

&lt;p&gt;The command surface is registered automatically via &lt;code&gt;SetMyCommands&lt;/code&gt;, so everything shows up in Telegram's &lt;code&gt;/&lt;/code&gt; menu. Highlights:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/start&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start the bot; first-time users pick a language (🇬🇧 / 🇮🇷).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/menu&lt;/code&gt;, &lt;code&gt;/help&lt;/code&gt;, &lt;code&gt;/cancel&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Navigation and flow control.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/users&lt;/code&gt;, &lt;code&gt;/adduser&lt;/code&gt;, &lt;code&gt;/adduser_interactive&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Paginated user list; quick or step-by-step user creation.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/server&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;View/edit server config field by field.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/blockedips&lt;/code&gt;, &lt;code&gt;/blockedaccess&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Manage blocked IPs and browse blocked-access logs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/logs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Browse access logs (last 20 per user).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/traffic&lt;/code&gt;, &lt;code&gt;/sessions&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Per-user traffic; list and revoke sessions.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/restart_server&lt;/code&gt;, &lt;code&gt;/restart_panel&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Restart either service, with confirmation.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Build and deploy
&lt;/h2&gt;

&lt;p&gt;From source you need Go 1.21+:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/ebrasha/abdal-4iproto-panel.git
&lt;span class="nb"&gt;cd &lt;/span&gt;abdal-4iproto-panel
go build &lt;span class="nt"&gt;-o&lt;/span&gt; abdal-4iproto-panel main.go      &lt;span class="c"&gt;# Linux&lt;/span&gt;
go build &lt;span class="nt"&gt;-o&lt;/span&gt; abdal-4iproto-panel.exe main.go  &lt;span class="c"&gt;# Windows&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a service, the binary self-registers. On Windows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;abdal-4iproto-panel.exe install
abdal-4iproto-panel.exe start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Linux with &lt;code&gt;systemd&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; ./install-abdal-4iproto-panel.sh
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;abdal-4iproto-panel
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status abdal-4iproto-panel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a hands-off setup, the &lt;a href="https://github.com/ebrasha/abdal-4iproto-cli" rel="noopener noreferrer"&gt;Abdal 4iProto Cli&lt;/a&gt; detects your OS/architecture, verifies SHA-256 checksums, configures ports, generates SSH keys, and registers persistent services.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Does the panel need a database?&lt;/strong&gt; No. State is stored in flat JSON files, which keeps deployment to a single binary and makes backups a file copy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I cap individual users?&lt;/strong&gt; Yes—per-user speed limits (KB/s), traffic quotas (MB), session limits, and TTL are all configurable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is the Telegram bot safe to expose?&lt;/strong&gt; It only accepts updates from explicitly listed admin IDs and rejects everything else. Treat the token like a secret and restrict admins tightly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Windows and Linux both?&lt;/strong&gt; Yes—single binary per platform, installable as a Windows Service or a Linux &lt;code&gt;systemd&lt;/code&gt; unit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing thought
&lt;/h2&gt;

&lt;p&gt;The design philosophy behind Abdal 4iProto Panel is that operational tooling should reduce moving parts, not add them. One binary, embedded assets, JSON state, and a concurrency model that isolates the bot from the UI—each decision trades novelty for fewer 2 a.m. surprises.&lt;/p&gt;

&lt;p&gt;Handcrafted with passion by &lt;strong&gt;Ebrahim Shafiei (EbraSha)&lt;/strong&gt; — 📧 &lt;a href="mailto:Prof.Shafiei@Gmail.com"&gt;Prof.Shafiei@Gmail.com&lt;/a&gt; · ✈️ &lt;a href="https://t.me/ProfShafiei" rel="noopener noreferrer"&gt;@ProfShafiei&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>vpn</category>
      <category>4iproto</category>
      <category>ssh</category>
    </item>
    <item>
      <title>Build a Self-Hosted SSH Tunneling Server with Per-User Accounting (Abdal 4iProto)</title>
      <dc:creator>Ebrahim Shafiei</dc:creator>
      <pubDate>Tue, 09 Jun 2026 22:02:29 +0000</pubDate>
      <link>https://dev.to/ebrasha/build-a-self-hosted-ssh-tunneling-server-with-per-user-accounting-abdal-4iproto-4c0k</link>
      <guid>https://dev.to/ebrasha/build-a-self-hosted-ssh-tunneling-server-with-per-user-accounting-abdal-4iproto-4c0k</guid>
      <description>&lt;h1&gt;
  
  
  Build a Self-Hosted SSH Tunneling Server with Per-User Accounting
&lt;/h1&gt;

&lt;p&gt;If you want a &lt;strong&gt;self-hosted SSH tunneling server&lt;/strong&gt; that handles bandwidth limits, session control, and brute-force protection without you scripting any of it, this walkthrough is for you. We'll set up Abdal 4iProto Server — a Go-based tunnel by &lt;strong&gt;Ebrahim Shafiei (EbraSha)&lt;/strong&gt; — from build to first connection, and I'll explain the config decisions along the way so you're not just copy-pasting blindly.&lt;/p&gt;

&lt;p&gt;Heads up: this server is part of the broader &lt;strong&gt;Abdal 4iProto&lt;/strong&gt; ecosystem (&lt;a href="https://github.com/4iProto" rel="noopener noreferrer"&gt;https://github.com/4iProto&lt;/a&gt;), so there are matching clients and tools once your server is up.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll have at the end
&lt;/h2&gt;

&lt;p&gt;A running tunnel server with per-user speed caps, total-traffic quotas, concurrent-session limits, automatic IP blocking on failed logins, and a SOCKS5 endpoint your apps can use. Let's go.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go 1.25.10 or higher&lt;/li&gt;
&lt;li&gt;An SSH private key (&lt;code&gt;id_rsa&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;A Linux or Windows host&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 2: Generate your host key
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; rsa &lt;span class="nt"&gt;-b&lt;/span&gt; 4096 &lt;span class="nt"&gt;-f&lt;/span&gt; id_rsa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Drop the resulting &lt;code&gt;id_rsa&lt;/code&gt; in the project directory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Write the server config
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;server_config.json&lt;/code&gt;. On Linux:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;64235&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;64236&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;64237&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"shell"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/bin/bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"max_auth_attempts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"server_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SSH-2.0-Abdal-4iProto-Server"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Windows, swap the shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;64235&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;64236&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;64237&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"shell"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cmd.exe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"max_auth_attempts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"server_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SSH-2.0-Abdal-4iProto-Server"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;ports&lt;/code&gt; array makes the server listen on several ports at once. &lt;code&gt;max_auth_attempts: 3&lt;/code&gt; means three strikes and the IP is auto-blocked. &lt;code&gt;server_version&lt;/code&gt; customizes the banner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Define your users
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;users.json&lt;/code&gt;. Here's an admin and a rate-limited user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ebrasha"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"change-me"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"blocked_domains"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"blocked_ips"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"log"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"no"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"max_sessions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"session_ttl_seconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"change-me-too"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"blocked_domains"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"facebook.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*.facebook.com"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"blocked_ips"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"10.0.0.*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"172.16.*.*"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"log"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"max_sessions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"session_ttl_seconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key fields to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;role&lt;/code&gt;: &lt;code&gt;admin&lt;/code&gt; gets host shell access; &lt;code&gt;user&lt;/code&gt; is tunnel-only. Keep day-to-day accounts as &lt;code&gt;user&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;blocked_domains&lt;/code&gt; / &lt;code&gt;blocked_ips&lt;/code&gt;: support wildcards (&lt;code&gt;*.facebook.com&lt;/code&gt;) and ranges (&lt;code&gt;10.0.0.*&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;log&lt;/code&gt;: per-user visited-site tracking — &lt;code&gt;"yes"&lt;/code&gt; or &lt;code&gt;"no"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;max_sessions&lt;/code&gt;: concurrent session ceiling.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;session_ttl_seconds&lt;/code&gt;: hard session lifetime before auto-reap.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To add bandwidth control, set &lt;code&gt;max_speed_kbps&lt;/code&gt; (e.g. &lt;code&gt;1024&lt;/code&gt; = 1 MB/s, enforced via token bucket on both directions) and &lt;code&gt;max_total_mb&lt;/code&gt; (e.g. &lt;code&gt;10240&lt;/code&gt; = 10 GB total quota). Quotas are checked every 1–2 seconds, and over-quota sessions are disconnected immediately — not just refused at next login.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Build and run
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go mod tidy
go build &lt;span class="nt"&gt;-o&lt;/span&gt; abdal-4iproto-server
./abdal-4iproto-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it — the server is listening.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Connect
&lt;/h2&gt;

&lt;p&gt;The quickest test is a standard dynamic-forwarding SSH client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-D&lt;/span&gt; 1080 username@server_ip &lt;span class="nt"&gt;-p&lt;/span&gt; 22
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now point any app at the local SOCKS5 proxy and your traffic tunnels through. On Linux you can route everything with &lt;code&gt;sshuttle&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sshuttle &lt;span class="nt"&gt;--dns&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; ebrasha@SERVER_IP:2222 0.0.0.0/0 &lt;span class="nt"&gt;-vv&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a GUI experience, grab the official clients: &lt;a href="https://github.com/ebrasha/abdal-4iproto-client" rel="noopener noreferrer"&gt;Windows&lt;/a&gt; and &lt;a href="https://github.com/ebrasha/abdal-4iproto-client-android" rel="noopener noreferrer"&gt;Android&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optional: relay traffic through another host with iptables NAT
&lt;/h2&gt;

&lt;p&gt;If you need a relay in front of your server, enable forwarding and NAT:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"net.ipv4.ip_forward=1"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/sysctl.conf
sysctl &lt;span class="nt"&gt;-p&lt;/span&gt;
iptables &lt;span class="nt"&gt;-t&lt;/span&gt; nat &lt;span class="nt"&gt;-A&lt;/span&gt; PREROUTING &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--dport&lt;/span&gt; 22 &lt;span class="nt"&gt;-j&lt;/span&gt; DNAT &lt;span class="nt"&gt;--to-destination&lt;/span&gt; IRAN_IP
iptables &lt;span class="nt"&gt;-t&lt;/span&gt; nat &lt;span class="nt"&gt;-A&lt;/span&gt; PREROUTING &lt;span class="nt"&gt;-j&lt;/span&gt; DNAT &lt;span class="nt"&gt;--to-destination&lt;/span&gt; 4iProto_IP
iptables &lt;span class="nt"&gt;-t&lt;/span&gt; nat &lt;span class="nt"&gt;-A&lt;/span&gt; POSTROUTING &lt;span class="nt"&gt;-j&lt;/span&gt; MASQUERADE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⚠️ &lt;strong&gt;Gotcha:&lt;/strong&gt; the SSH-port DNAT rule must come &lt;em&gt;before&lt;/em&gt; the general PREROUTING rule, and confirm you have alternate SSH access before applying these — it's an easy way to lock yourself out.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's happening under the hood
&lt;/h2&gt;

&lt;p&gt;A few implementation notes worth knowing as you operate this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rate limiting uses a &lt;strong&gt;token-bucket&lt;/strong&gt; algorithm, which smooths throughput while allowing short bursts — friendlier to interactive sessions than a hard window.&lt;/li&gt;
&lt;li&gt;Traffic accounting flushes to &lt;code&gt;traffic_&amp;lt;username&amp;gt;.json&lt;/code&gt; every 10 seconds, so a crash costs at most a few seconds of stats.&lt;/li&gt;
&lt;li&gt;Failed logins go to &lt;code&gt;invalid_logins.log&lt;/code&gt; with the attempted credentials and IP; blocked IPs persist in &lt;code&gt;blocked_ips.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;TCP and UDP forwarding are both supported, plus DNSTT for DNS tunneling on hostile networks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The full ecosystem
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Repository&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🔐 Server&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/ebrasha/abdal-4iproto-server" rel="noopener noreferrer"&gt;abdal-4iproto-server&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;⚙️ Web Panel&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/ebrasha/abdal-4iproto-panel" rel="noopener noreferrer"&gt;abdal-4iproto-panel&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;💻 Windows Client&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/ebrasha/abdal-4iproto-client" rel="noopener noreferrer"&gt;abdal-4iproto-client&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📱 Android Client&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/ebrasha/abdal-4iproto-client-android" rel="noopener noreferrer"&gt;abdal-4iproto-client-android&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🖱️ CLI Installer&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/ebrasha/abdal-4iproto-cli" rel="noopener noreferrer"&gt;abdal-4iproto-cli&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🗝️ SSH KeyGen&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/ebrasha/abdal-4iproto-server-ssh-keygen" rel="noopener noreferrer"&gt;abdal-4iproto-server-ssh-keygen&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Tip: the &lt;a href="https://github.com/ebrasha/abdal-4iproto-cli" rel="noopener noreferrer"&gt;CLI installer&lt;/a&gt; automates OS/arch detection, SHA-256 verification, port config, key generation, and service registration if you'd rather skip the manual build.&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://github.com/ebrasha/abdal-4iproto-server" rel="noopener noreferrer"&gt;https://github.com/ebrasha/abdal-4iproto-server&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Do I need the GUI clients?&lt;/strong&gt; No — any SSH client with &lt;code&gt;-D&lt;/code&gt; dynamic forwarding works. The clients just make it nicer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I cap a user's speed?&lt;/strong&gt; Add &lt;code&gt;max_speed_kbps&lt;/code&gt; to their &lt;code&gt;users.json&lt;/code&gt; entry (&lt;code&gt;1024&lt;/code&gt; = 1 MB/s).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens when a user hits their data quota?&lt;/strong&gt; They're disconnected mid-session within 1–2 seconds and refused at next login until the quota resets.&lt;/p&gt;

</description>
      <category>socks5</category>
      <category>4iproto</category>
      <category>abdal</category>
      <category>ssh</category>
    </item>
    <item>
      <title>Build a Device-Wide SSH Tunnel on Android with Abdal 4iProto Android</title>
      <dc:creator>Ebrahim Shafiei</dc:creator>
      <pubDate>Tue, 09 Jun 2026 20:52:46 +0000</pubDate>
      <link>https://dev.to/ebrasha/build-a-device-wide-ssh-tunnel-on-android-with-abdal-4iproto-25a4</link>
      <guid>https://dev.to/ebrasha/build-a-device-wide-ssh-tunnel-on-android-with-abdal-4iproto-25a4</guid>
      <description>&lt;p&gt;Build a Device-Wide SSH Tunnel on Android with Abdal 4iProto&lt;br&gt;
If you've ever run ssh -D 1080 and pointed your browser at a local SOCKS5 proxy, you already understand half of what this app does. The other half is the interesting part: doing it for every app on your phone, with no root, and with leak protection that actually holds up. Let's walk through it hands-on.&lt;br&gt;
Abdal 4iProto Android is the Android client of the open-source Abdal 4iProto ecosystem by Ebrahim Shafiei (EbraSha). It builds a device-wide SSH tunnel on Android, routing all traffic through an encrypted connection to your own server. No third-party VPN provider, no per-app juggling.&lt;br&gt;
TL;DR of the data flow&lt;br&gt;
Here's the path a packet takes, start to finish:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; App → VpnService (TUN, raw IP packets)
    → hev-socks5-tunnel (native tun2socks via JNI)
    → local SOCKS5 bridge
    → SSH direct-tcpip channel (JSch)
    → your Abdal 4iProto Server → Internet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fetf2lwoqpe7kh3jolvly.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fetf2lwoqpe7kh3jolvly.jpg" alt=" " width="800" height="1778"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The trick is that SSH forwards TCP streams, but the OS gives you raw IP packets. The native tun2socks engine bridges that gap by turning packets into SOCKS5 connections, which then get forwarded over SSH.&lt;br&gt;
Step 1: Stand up a server&lt;br&gt;
This is a client, so you need the other end. Grab the server component from the ecosystem repo and get it running first. You'll need its IP/hostname, port, and an SSH username/password.&lt;br&gt;
Step 2: Install and add a server&lt;br&gt;
Open the app, then:&lt;/p&gt;

&lt;p&gt;Hamburger menu → Server Management → Add Server&lt;br&gt;
Enter a friendly name, your server's IP/hostname, port, username, and password&lt;br&gt;
Select the server on the main screen&lt;/p&gt;

&lt;p&gt;You can store multiple named servers and switch between them — handy if you run more than one.&lt;br&gt;
Step 3: Configure before connecting&lt;br&gt;
A gotcha worth flagging up front: connection options are applied at connect time, so toggle them before you tap Connect.&lt;br&gt;
The ones worth knowing:&lt;/p&gt;

&lt;p&gt;Fake-IP / FakeDNS — resolves domains on the server (remote DNS) so no real DNS query leaves your device. Turn this on to close the DNS-leak gap. With it off, you still get tunneled DNS-over-TCP.&lt;br&gt;
Kill Switch — blocks all traffic if the tunnel drops unexpectedly, with no leak gap, until it reconnects or you disconnect.&lt;br&gt;
Advanced Settings → Whitelist IPs / CIDR — comma-separated IPs or CIDR blocks that should bypass the tunnel.&lt;/p&gt;

&lt;p&gt;Step 4: Per-app split tunneling (optional)&lt;br&gt;
Open the Per-App Split Tun screen and pick a mode:&lt;/p&gt;

&lt;p&gt;Route via Tunnel → only the apps you select are tunneled.&lt;br&gt;
Bypass Tunnel → everything is tunneled except the apps you select.&lt;/p&gt;

&lt;p&gt;The list is searchable and each app gets its own toggle. Apps kept off the tunnel are also exempt from the kill switch, so they keep normal connectivity.&lt;br&gt;
Step 5: Connect&lt;br&gt;
Tap Connect and grant the VPN permission when Android prompts. That permission is what lets the app stand up its VpnService TUN interface. Once it's up, your whole device is tunneled. Watch the real-time logs from the menu to confirm what's happening.&lt;br&gt;
Tap Disconnect to tear it down.&lt;br&gt;
Why it doesn't leak&lt;br&gt;
Two implementation details do the heavy lifting here, and they're worth understanding if you build networking apps yourself:&lt;/p&gt;

&lt;p&gt;The control socket is protected. The SSH connection itself is wrapped in VpnService.protect() so it doesn't get routed back into the TUN interface. Skip this and you get a routing loop that kills reconnects.&lt;br&gt;
Local ranges bypass automatically. Private/LAN ranges (192.168.x.x, 10.x.x.x) skip the tunnel, so your router, printer, and any phone-hosted services keep working. On API 33+ this uses excludeRoute().&lt;/p&gt;

&lt;p&gt;The stack, quickly&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="err"&gt;minSdk&lt;/span&gt; &lt;span class="err"&gt;24&lt;/span&gt; &lt;span class="err"&gt;(Android&lt;/span&gt; &lt;span class="err"&gt;7.0)&lt;/span&gt;  &lt;span class="err"&gt;·&lt;/span&gt;  &lt;span class="err"&gt;compileSdk/targetSdk&lt;/span&gt; &lt;span class="err"&gt;36&lt;/span&gt; &lt;span class="err"&gt;(Android&lt;/span&gt; &lt;span class="err"&gt;16)&lt;/span&gt;
&lt;span class="err"&gt;applicationId&lt;/span&gt; &lt;span class="err"&gt;net.abdal.abdal4iproto.client&lt;/span&gt;  &lt;span class="err"&gt;·&lt;/span&gt;  &lt;span class="err"&gt;versionName&lt;/span&gt; &lt;span class="err"&gt;5.2&lt;/span&gt;

&lt;span class="err"&gt;Kotlin&lt;/span&gt; &lt;span class="err"&gt;2.2.10&lt;/span&gt; &lt;span class="err"&gt;·&lt;/span&gt; &lt;span class="err"&gt;AGP&lt;/span&gt; &lt;span class="err"&gt;9.1.1&lt;/span&gt; &lt;span class="err"&gt;·&lt;/span&gt; &lt;span class="err"&gt;Gradle&lt;/span&gt; &lt;span class="err"&gt;9.3.1&lt;/span&gt; &lt;span class="err"&gt;·&lt;/span&gt; &lt;span class="err"&gt;KSP&lt;/span&gt; &lt;span class="err"&gt;2.3.5&lt;/span&gt; &lt;span class="err"&gt;·&lt;/span&gt; &lt;span class="err"&gt;Java&lt;/span&gt; &lt;span class="err"&gt;11&lt;/span&gt;
&lt;span class="py"&gt;UI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Jetpack Compose (Material 3), Navigation Compose 2.8.9&lt;/span&gt;
&lt;span class="py"&gt;SSH&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;JSch (mwiede fork) 0.2.21&lt;/span&gt;
&lt;span class="py"&gt;Crypto&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bouncy Castle bcprov-jdk18on 1.84  → ssh-ed25519 host keys&lt;/span&gt;
&lt;span class="py"&gt;Coroutines&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kotlinx-coroutines 1.10.2&lt;/span&gt;
&lt;span class="py"&gt;Persistence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Room 2.7.0 (KSP)&lt;/span&gt;
&lt;span class="py"&gt;Native&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hev-socks5-tunnel (.so) tun2socks via JNI (TProxyService)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The native tun2socks engine is the deliberate performance call — packet translation stays in compiled native code instead of crossing the JNI boundary per packet in managed Kotlin.&lt;br&gt;
Wrapping up&lt;br&gt;
The whole design comes down to one principle: capture everything, leak nothing, and keep the trust boundary at your server. No telemetry, open source, auditable via live logs. If you've been meaning to graduate from per-app proxies to a real device-wide tunnel you actually control, this is a clean way to do it.&lt;br&gt;
FAQ&lt;br&gt;
Do I need root? No — it uses VpnService.&lt;br&gt;
Why does my home network still work while connected? Private/LAN ranges bypass the tunnel by design.&lt;br&gt;
What's the minimum Android version? API 24. Some routing features need API 33+.&lt;br&gt;
Can I tunnel only specific apps? Yes — use the Per-App Split Tun screen's "Route via Tunnel" mode.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ebrasha/abdal-4iproto-client-android" rel="noopener noreferrer"&gt;https://github.com/ebrasha/abdal-4iproto-client-android&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🤵 Programmer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Handcrafted with Passion by Ebrahim Shafiei (EbraSha)&lt;/p&gt;

</description>
      <category>android</category>
      <category>ssh</category>
      <category>security</category>
      <category>networking</category>
    </item>
  </channel>
</rss>
