Skirk Setup Guide
May 21, 2026 ยท View on GitHub
This is the operator flow:
- Install
skirkon the exit/setup machine. - Run
skirk setup init --out skirk-kit --reset-google-login. - Open the Google URL printed by setup, enter the short code, and approve.
- Setup installs/enables
skirk-exit.serviceand starts the exit on Linux. - Send only
skirk-kit/client.skirkor its one-line text to clients.
The exit machine needs outbound internet access. It does not need an inbound port because both sides exchange encrypted objects through Google Drive.
Install
curl -fsSL https://raw.githubusercontent.com/ShahabSL/Skirk/main/install.sh | sh
skirk version
From a source checkout:
make build
./bin/skirk version
Create A Kit
Reliable new-install path:
skirk setup init --out skirk-kit --reset-google-login
On Linux this starts the exit as skirk-exit.service after Google login and
kit generation. Use --start-exit=false for config-only setup,
--exit-service-name NAME to choose a different systemd unit name,
--exit-service-user USER to run the service as a specific account, or
--exit-service-enable=false to start it now without enabling boot startup.
Google blocks the default Google Cloud SDK OAuth client when a third-party app
requests Drive scopes. That is the browser page that says "This app is blocked".
Skirk therefore does not launch the default gcloud Drive-scope login for new
credentials. Official release builds use Skirk's OAuth client directly. Source
or development builds can set SKIRK_OAUTH_CLIENT_ID, optionally
SKIRK_OAUTH_CLIENT_SECRET when Google provides one, pass
--oauth-client-file, or pass --adc with credentials you created yourself.
Run setup from an interactive terminal. For SSH, use ssh -tt -p PORT user@host
if the server does not allocate a TTY by default.
If setup cannot contact Google's OAuth endpoints, one common VPS cause is broken IPv6: the server resolves Google OAuth to IPv6 addresses but cannot actually connect over IPv6. Check it on the server:
curl -4 --connect-timeout 5 --max-time 15 https://oauth2.googleapis.com/token
curl -6 --connect-timeout 5 --max-time 15 https://oauth2.googleapis.com/token
If IPv4 returns quickly but IPv6 times out, prefer IPv4 before rerunning setup:
sudo sh -c 'grep -q "^precedence ::ffff:0:0/96 100" /etc/gai.conf || echo "precedence ::ffff:0:0/96 100" >> /etc/gai.conf'
skirk setup init --out skirk-kit --reset-google-login
This uses Google's OAuth device authorization flow. The terminal prints a URL and a short code. Open the URL in your browser, enter the code there, approve Drive access, and the terminal continues without a paste-back step.
Skirk requests only the Drive mailbox scope it needs:
https://www.googleapis.com/auth/drive.file
The Google device-code flow accepts drive.file, which lets Skirk create and
manage only files and folders created by the Skirk app. Setup creates a
skirk-mailbox-... Drive folder and writes that folder ID into the generated
exit and client configs. This is the public easy-install path because it avoids
the blocked Google Cloud SDK OAuth client and does not require each user to
create an OAuth app.
Personal Quota Mode
The built-in Skirk OAuth client is the easiest way to try Skirk. For sustained video use, multiple clients, forks, or deployments that must not share Skirk's Google Cloud project quota, create a personal OAuth client and pass it to setup.
With the built-in client:
- setup is easiest;
- Drive API usage is charged to Skirk's shared Google Cloud project quota;
- each Google account still has its own per-user-per-project quota bucket.
With a personal OAuth client:
- setup uses the user's own Google Cloud project;
- Drive API usage is charged to that project quota;
- the generated Skirk configs still use only the same
drive.filemailbox scope.
To create a personal Skirk OAuth client:
- Create or select a Google Cloud project.
- Enable Google Drive API.
- Configure the OAuth consent screen and publish it if you want refresh tokens that do not expire after the testing window.
- Add your Google account as a test user if the app is in testing mode.
- Create an OAuth client ID with application type
Desktop app. - Download the client JSON as
oauth-client.json, or copy the client ID and client secret from the client details page.
Then run:
skirk setup init --out skirk-kit --reset-google-login --oauth-client-file ./oauth-client.json
The guided equivalent is:
skirk setup init --out skirk-kit --reset-google-login --oauth-mode personal
The personal OAuth wizard prints the exact Google Cloud pages to open:
- Create or select a Google Cloud project.
- Enable Google Drive API.
- Configure the OAuth consent screen and add your Google account as a test user if the app stays in testing mode.
- Create an OAuth client with application type
Desktop app. - Paste the client ID and client secret into Skirk. You can also download the JSON.
If you paste the client ID and secret, setup writes the ignored local
oauth-client.json file for you. Skirk then prints a Google approval URL. On a
VPS over SSH, the browser usually redirects to a localhost URL that cannot load;
copy that full address-bar URL back into the terminal and Skirk extracts the
authorization code.
Do not choose TVs and Limited Input devices for personal setup unless Google
also provides a client secret and you intentionally run setup with
--oauth-flow device. Google's device-code token polling requires
client_secret; a TV client that only shows a client ID cannot complete setup.
If Google shows Access blocked and says the app is currently being tested,
open https://console.cloud.google.com/auth/audience, add the exact Google
account shown on the blocked page under Test users, then rerun setup. Publishing
the app to Production also removes the Testing allowlist block, although Google
can still show unverified-app warnings and user caps until verification. Do not
add extra scopes for this error; Skirk requests drive.file during Google login.
Or set environment variables for local builds:
SKIRK_OAUTH_CLIENT_ID='...' \
SKIRK_OAUTH_CLIENT_SECRET='...' \
SKIRK_OAUTH_FLOW=desktop \
skirk setup init --out skirk-kit --reset-google-login
SKIRK_OAUTH_FLOW=desktop is the personal-project path that works cleanly on a
VPS by pasting back the redirected localhost URL.
If Google blocks your OAuth client, enable the Google Drive API, configure the OAuth consent screen, add the signing-in Google account as a test user while the app is in testing, or publish/verify the app according to Google policy. Skirk cannot bypass a Google account or OAuth enforcement decision.
Google Drive API project limits can be increased for some quota types from the Google Cloud Quotas page, but increases are not guaranteed. Some limits are not adjustable, including Google's per-user Drive upload limit and the daily billing threshold documented for the Drive API.
This split mirrors mature Drive tools such as rclone: default shared OAuth keeps setup easy, while personal OAuth isolates quota for high-volume users.
Generated Files
skirk-kit/exit.json:
Keep this on the exit machine. It contains credentials.
skirk-kit/client.skirk:
One-line profile for client devices. This is the easiest thing to paste into
Linux, Windows, or Android clients. You can import the same profile on more
than one device. Skirk gives each imported profile a local client identity, and
each connection run gets a fresh run identity, so simultaneous devices do not
claim each other's Drive responses.
skirk-kit/client.json:
JSON form of the same client profile.
skirk-kit/client-command.txt:
Ready-to-copy Linux client command with the one-line profile embedded.
skirk-kit/README.md:
Per-kit commands generated at setup time.
All generated profiles contain a Google refresh token and the Skirk tunnel secret. Do not commit them or paste them into public logs.
Run The Exit
skirk service status
Useful exit options:
# If setup was run with --start-exit=false, start manually.
skirk serve-exit --config skirk-kit/exit.json
# Send exit traffic through another local proxy.
skirk serve-exit --config skirk-kit/exit.json --exit-proxy socks5h://127.0.0.1:40000
# Override Drive worker windows for experiments.
skirk serve-exit --config skirk-kit/exit.json --upload-concurrency 16 --download-concurrency 32
For generated kits, prefer writing the exit proxy into exit.json during
setup:
skirk setup init --out skirk-kit --reset-google-login --exit-proxy socks5h://127.0.0.1:40000
Multiple Exit Instances
One Linux exit machine can run more than one Skirk instance when you want to
split traffic across multiple OAuth projects or Drive mailboxes. Run skirk
and choose Manage exit services and instances:
Create another Skirk exit instancegenerates a separate kit under~/.config/skirk/instances/<id>/and installs a service namedskirk-exit-<id>by default.Manage an existing exit instanceshows status, start/stop/restart, outbound proxy/WARP configuration, Drive cleanup, and per-instance performance tuning.Delete this instancehas separate prompts for service removal, Drive object deletion, OAuth revocation, and local kit deletion. It does not remove the Skirk binary.
The original skirk-kit/exit.json plus skirk-exit.service flow remains the
default simple setup.
When using install.sh, SKIRK_INSTALL_WIREPROXY=1 installs Skirk-managed
wgcf/wireproxy under fixed system paths, starts wireproxy.service as the
unprivileged skirk-wireproxy user, and defaults SKIRK_EXIT_PROXY to the
local loopback SOCKS listener. Managed WARP binds only to 127.0.0.1:PORT;
use an explicit --exit-proxy or SKIRK_EXIT_PROXY for a different
already-secured outbound proxy. SKIRK_ACCEPT_WARP_TOS=1 makes WARP
registration noninteractive.
serve-exit starts a mailbox janitor automatically. It runs at startup and then
every 2 minutes, removing stale mux transport, benchmark, and setup-marker
objects older than 10 minutes. Normal processed mux objects are deleted by the
foreground-aware runtime cleanup path; the janitor is intentionally conservative
and uses low delete concurrency so slow Drive uploads cannot cause live stream
frames to be deleted as stale, but long VPN or multi-client sessions still get
stale-object cleanup. Override with:
SKIRK_JANITOR_OLDER_THAN=6h SKIRK_JANITOR_INTERVAL=1h skirk serve-exit --config skirk-kit/exit.json
SKIRK_DISABLE_JANITOR=1 skirk serve-exit --config skirk-kit/exit.json
Run A Linux Client
With a file:
skirk serve-client --config client.skirk --listen 127.0.0.1:18080
For repeated CLI use on the same machine, set a stable client ID. Desktop and Android apps do this automatically when importing a profile:
skirk serve-client --config client.skirk --listen 127.0.0.1:18080 --client-id my-laptop
With pasted text:
read -r SKIRK_CLIENT_CONFIG
skirk serve-client --config "$SKIRK_CLIENT_CONFIG" --listen 127.0.0.1:18080
Test:
curl --socks5-hostname 127.0.0.1:18080 http://example.com/
Use socks5h semantics in apps that support it so DNS resolution happens
through the exit.
Optional HTTP/HTTPS proxy listener:
skirk serve-client \
--config "$SKIRK_CLIENT_CONFIG" \
--listen 127.0.0.1:18080 \
--http-proxy-listen 127.0.0.1:18081
For trusted-LAN sharing, bind both listeners to 0.0.0.0 and restrict access
with host firewall rules:
skirk serve-client \
--config "$SKIRK_CLIENT_CONFIG" \
--listen 0.0.0.0:18080 \
--http-proxy-listen 0.0.0.0:18081
Clients can also override Drive runtime behavior:
skirk serve-client \
--config "$SKIRK_CLIENT_CONFIG" \
--listen 127.0.0.1:18080 \
--poll-ms 1000 \
--upload-concurrency 8 \
--download-concurrency 16 \
--metrics-path /tmp/skirk-client.metrics.json
Desktop and Android clients expose this through presets and show the same local metrics estimate in the UI. Treat it as local burn rate only, not remaining project quota.
Restricted Networks
Generated client profiles default to google_front_pinned. That route uses a
Google-looking TLS/SNI path pinned to the configured Google edge IP for Google
API traffic, which is the current default for the tested hostile network. The
exit route defaults to direct.
Existing Linux profiles generated before v0.1.51 are not rewritten by an
update. Regenerate the kit or pass
--route-mode google_front_pinned --google-ip 216.239.38.120 when starting the
client.
When the hostile network is available through a local SOCKS proxy:
skirk serve-client \
--config "$SKIRK_CLIENT_CONFIG" \
--listen 127.0.0.1:18080 \
--route-mode google_front_pinned \
--upstream-proxy socks5h://127.0.0.1:11093
For direct Google API routing on a normal network:
skirk serve-client --config "$SKIRK_CLIENT_CONFIG" --listen 127.0.0.1:18080 --route-mode direct
Available route modes:
directreal_pinnedgoogle_frontgoogle_front_pinnedgoogle_front_h1google_front_h1_pinned
Pinned modes use the configured --google-ip value.
Benchmarks
The exit must be running before client-side benchmarks.
skirk bench-live --config skirk-kit/client.skirk --samples 5
Hostile path:
skirk bench-live \
--config skirk-kit/client.skirk \
--upstream-proxy socks5h://127.0.0.1:11093 \
--route-mode google_front_pinned \
--samples 3
Bulk throughput:
skirk bench-live --config skirk-kit/client.skirk --bulk-url http://example.com/big.bin --timeout 5m
The JSON result includes p50/p95 latency, Mbps, Drive operation timings, and estimated quota calls/units per request and per minute.
Cleanup
Runtime cleanup is enabled in generated configs. Processed mux objects are deleted after use, with foreground traffic prioritized over cleanup. The exit janitor removes stale leftovers from crashed clients or interrupted exits.
Dry-run stale object cleanup:
skirk cleanup --config skirk-kit/exit.json --older-than 2h
Delete stale objects:
skirk cleanup --config skirk-kit/exit.json --older-than 2h --delete
Clean a specific prefix:
skirk cleanup --config skirk-kit/exit.json --prefix muxv4/ --older-than 1s --delete
Empty every object in this Skirk mailbox:
skirk cleanup --config skirk-kit/exit.json --all --older-than 1ns --delete --max-pages 20000
If the configured Drive mailbox folder was deleted or the logs show
drive_not_found, create a replacement folder, rewrite the local kit, and
restart the exit service:
skirk repair-mailbox --kit skirk-kit --start-exit
Disconnect Or Revoke
Stop the client and exit processes first.
If the exit is running as a systemd service:
skirk service stop --name skirk-exit
Revoke the OAuth refresh token embedded in the generated config:
skirk revoke --config skirk-kit/exit.json --revoke-oauth
Delete stale Drive mailbox objects and then remove local generated files:
skirk cleanup --config skirk-kit/exit.json --all --older-than 1ns --delete --max-pages 20000
rm -rf skirk-kit
OAuth revocation invalidates configs generated from that token. If you no longer have the config, revoke the app from the Google account security page.
The interactive menu (skirk with no arguments) exposes the same setup,
service, cleanup, revoke, and local delete actions.
Learning Notes
Skirk uses Google Drive as an object mailbox, not as a stream. The production shape is a bounded mux: many TCP streams are encoded into encrypted Drive mux objects, then reassembled by the exit. Mux v4 also namespaces objects by local client ID and per-run ID, which is the normal distributed-systems pattern for sharing one credential/profile across multiple independent devices without cross-delivery. This avoids one polling loop per browser connection, which is the failure mode that makes naive Drive proxy designs collapse under real browsing.
Why This Matters
The operational risk is not just speed. Leftover mailbox objects consume Google Drive storage, leaked profiles are credentials, and excessive API calls can hit Drive rate limits. The setup, cleanup, janitor, and benchmark commands are part of the production surface, not side utilities.