Open-source Hubfly CLI written in Go.
Repo: https://github.com/hubfly-space/hubfly-cli
curl -fsSL https://raw.githubusercontent.com/hubfly-space/hubfly-cli/main/install.sh | bashVerify the installation:
hubfly versionOpen Windows PowerShell or PowerShell in Windows Terminal and run:
irm https://raw.githubusercontent.com/hubfly-space/hubfly-cli/main/install.ps1 | iex
hubfly version
hubfly loginThe installer automatically selects the Windows AMD64 (x64) or ARM64 release,
verifies its SHA-256 checksum, and installs hubfly.exe to
%LOCALAPPDATA%\HubFly\bin. It adds that directory to your user PATH and the
current PowerShell session. The default installation does not require an
administrator terminal. Reopen any other terminals to pick up the PATH change.
For a custom installation directory, download and run the installer:
$installer = Join-Path $env:TEMP 'hubfly-install.ps1'
Invoke-WebRequest -UseBasicParsing `
-Uri 'https://raw.githubusercontent.com/hubfly-space/hubfly-cli/main/install.ps1' `
-OutFile $installer
& $installer -InstallDir "$env:USERPROFILE\Tools\HubFly"To install a specific published release, also pass -Version <release-tag>
(replace <release-tag> with the desired tag from the
releases page).
If PowerShell blocks the downloaded script, run it with an execution-policy override for that process, then open a new terminal:
powershell.exe -NoProfile -ExecutionPolicy Bypass -File $installerIf hubfly is not recognized after installation, check the binary directly:
& "$env:LOCALAPPDATA\HubFly\bin\hubfly.exe" versionFor a custom installation, use that directory instead. Confirm it is in your user PATH and reopen the terminal.
Download hubfly_windows_amd64.zip for x64 or hubfly_windows_arm64.zip for
ARM64, plus its matching .sha256 file, from the
releases page.
In the download directory, verify and extract the archive (change $asset for
ARM64):
$asset = 'hubfly_windows_amd64.zip'
$expected = ((Get-Content -Raw "$asset.sha256").Trim() -split '\s+')[0]
$actual = (Get-FileHash -Algorithm SHA256 $asset).Hash
if ($expected -notmatch '^[a-fA-F0-9]{64}$' -or $actual -ne $expected) {
throw 'Release archive checksum verification failed.'
}
$installDir = Join-Path $env:LOCALAPPDATA 'HubFly\bin'
Expand-Archive -LiteralPath $asset -DestinationPath $installDir -Force
& "$installDir\hubfly.exe" versionAdd $installDir to your user PATH and open a new terminal to run hubfly
from any directory. Docker with Linux containers enabled is required for local
image builds; installation and remote management commands can run without it.
- Token-based auth (
login,logout,whoami) - Persistent Bubble Tea TUI for project/container/tunnel workflow (
projects) - Single and multiple tunnel connect flows
- SSH, one-shot command exec, and log streaming for containers
- Local-first stack orchestration for Compose-style apps (
stack) - Local tunnel status screens while sessions are active
- Local
hubfly deployflow with reusablehubfly-builder,hubfly.build.json, deploy diffs, and resumable image uploads - Build config helpers:
hubfly build init|validate|edit|explain - Debug mode for API requests/responses (
--debugorHUBFLY_DEBUG=1) - Managed Realtime servers, apps, keys, bindings, webhooks, events, live inspector and E2E key generation (
realtime, aliasrt) - Managed Databases (PostgreSQL, MySQL, SQLite/libSQL): create, connect, query, users, network, app bindings, export/import (
db) - Object Storage: storage projects, buckets, object upload/download, S3 access keys, published assets and video encoding (
storage) - Built-in version and self-update commands
- Tunnel service mode (
service) - GitHub Pages documentation page in
index.html
hubfly login [--token <TOKEN>]
hubfly logout
hubfly whoami
hubfly projects
hubfly deploy [advanced|--advanced] [--project <id|name|new>] [--region <region>] [--yes]
[--config <path>] [--detach] [--dockerfile <path>] [--builder-version <tag>]
hubfly stack plan [--file <compose-file>]
hubfly stack up [--file <compose-file>] [--project <id|name|new>] [--region <region>] [--yes] [--remove-orphans] [--no-build]
hubfly stack status [--file <compose-file>]
hubfly stack logs [service...] [--follow|-f]
hubfly stack exec <service> -- <cmd> [args...]
hubfly stack ssh <service>
hubfly stack down [--file <compose-file>] [--volumes] [--yes]
hubfly build init [--config <path>] [--dockerfile <path>] [--force] [--print]
hubfly build validate [--config <path>] [--dockerfile <path>] [--builder-version <tag>] [--json]
hubfly build edit [--config <path>]
hubfly build explain [--config <path>] [--dockerfile <path>] [--builder-version <tag>] [--json]
hubfly tunnel <containerIdOrName> <localPort> <targetPort>
hubfly ssh <containerIdOrName> [-- <cmd> [args...]]
hubfly exec <containerIdOrName> -- <cmd> [args...]
hubfly logs <containerIdOrName> [--follow|-f]
hubfly orgs
hubfly version
hubfly update --check
hubfly update
hubfly service [--port <port>]
hubfly realtime <list|info|create|delete|start|stop|restart|resize|apps|keys|bind|env|publish|send|tail|webhooks|channels|history|insights|keygen|...> [options]
hubfly db <list|engines|info|create|delete|start|stop|restart|resize|url|env|connect|shell|query|users|databases|network|bind|export|import|...> [options]
hubfly storage <list|create|info|usage|buckets|ls|put|get|rm|url|keys|publish|share|encode|...> [options]hubfly realtime (alias rt) manages HubFly Managed Realtime: Pusher-compatible
WebSocket servers with HubFly extensions and built-in end-to-end encryption.
<server> is a realtime server id or name (add --project <id|name> when two
projects use the same name). <app> is an app id or name; --app may be omitted
when the server has one app. Every command that prints data accepts --json.
hubfly rt list [--project <id|name>]
hubfly rt info chat
hubfly rt create chat --size small --project new # quotes the monthly price and asks to confirm
hubfly rt create chat --size small --project my-managed --yes # scripts: --yes accepts the quoted price
hubfly rt delete chat # type the name to confirm, or --yescreate reads the sizes from the managed catalog, quotes the price from the
pricing endpoint ($x.xx/month, billed hourly), asks for confirmation unless
--yes is given, creates the server (in a new managed project with
--project new; HubFly chooses the region of managed projects) and waits for the operation with a progress spinner. Pass
--no-wait to return right away, --cpu/--memory for a custom size and
--app <name> to name the first app (default production).
Apps, keys and bindings:
hubfly rt apps chat
hubfly rt apps create chat staging --origins https://app.example.com --encryption required
hubfly rt apps update chat staging --client-events --history durable --history-ttl 1h
hubfly rt apps delete chat staging
hubfly rt keys list chat --app staging
hubfly rt keys create chat --app staging # prints the new secret once
hubfly rt keys rotate chat --app staging --grace 24h
hubfly rt keys revoke chat <keyId> --app staging
hubfly rt keys reveal chat --app staging # audited secret reveal
hubfly rt bind chat --to web --preset laravel --client-prefix VITE_
hubfly rt bind list chat
hubfly rt bind remove chat <bindingId> # removes the variables it wrote; --keep-variables keeps them
hubfly rt env chat --app staging --format dotenv # add --reveal to include the secretApp settings flags for apps create/apps update: --origins <a,b>,
--client-events[=false], --subscription-count[=false], --require-auth[=false],
--encryption off|optional|required, --history off|memory|durable,
--history-ttl <duration>, --history-max <n>, --offline-queue[=false],
--max-connections <n>, --max-message-bytes <n> and --payload-preview[=false].
Secrets are printed only by keys create, keys reveal, and by --reveal on
apps create, keys rotate and env. keys create always prints the new
secret, the one time it is shown (as in the web dialog): in text mode with a
warning on stderr, with --json in the secret field. keys rotate shows how long the old key
keeps working (the grace period). --debug never prints secrets or tokens.
Events and debugging:
hubfly rt publish chat --channel orders --event order.updated --data '{"id":42}'
echo '{"id":42}' | hubfly rt publish chat orders order.updated
hubfly rt send chat --connection 1234567890.1234567890 --event notice --data hello
hubfly rt send chat --user u_42 --event notice --data-file ./notice.json
hubfly rt tail chat --family msg,conn --channel-prefix orders
hubfly rt tail chat --json | jq .
hubfly rt insights chat --window 6hEvent data is sent as text from --data, --data-file <path|-> or stdin.
End-to-end connection ids (<socket_id>~<key>) carry a client key that must
never reach HubFly: the CLI removes the key before any request. Plaintext sends
to such ids are refused; encrypt end-to-end direct messages on your own server
with the HubFly server SDK (or pass --strip-key to send unencrypted plaintext
to the socket id).
tail opens the live inspector. It gets a short-lived inspector token from the
Dashboard, connects to wss://<host>/hubfly/v1/inspect with the token's origin
(the API origin when the token names none) as Origin, sends the token in the first frame, and reconnects with a new token
when the session ends. It prints one line per event (--json for JSON lines)
and notes sampling and dropped events on stderr. Press Ctrl-C to stop.
End-to-end master keys are generated locally and never sent anywhere:
hubfly rt keygen --kid k2 # k2:<base64 of 32 random bytes>Put the key first in HUBFLY_REALTIME_E2E_KEYS (comma-separated kid:base64
entries; the first one encrypts) on your own servers.
Manage the server lifecycle and API allowlist:
hubfly rt stop chat
hubfly rt start chat
hubfly rt restart chat
hubfly rt resize chat --size small --yes
hubfly rt upgrade chat --yes
hubfly rt rename chat team-chat
hubfly rt protect team-chat on
hubfly rt network allow team-chat --my-ip --label office
hubfly rt network remove team-chat 203.0.113.10/32Commands wait for the operation unless you pass --no-wait. Restarts use an
in-place handoff that preserves connections; resize applies live. Upgrades and
stops drain clients. The network allowlist guards the server HTTP API;
WebSocket clients are never IP-restricted. Realtime servers always have public
access. Use --json for structured lifecycle output and --timeout to limit
how long to wait.
Logs, metrics and operations (the same commands work for hubfly db):
hubfly rt logs chat # last 64 KB of the server's stdout and stderr
hubfly rt logs chat --tail 512k --follow # polls every 2 s and prints new lines; Ctrl-C stops
hubfly rt metrics chat --window 6h # CPU, memory, storage, network, connections, channels, messages
hubfly rt operations chat --limit 10 # create, restart, resize, upgrade, ... with status and errors--tail takes bytes (65536, 64k, 1m; 4 KB to 2 MB). --window takes 5m to 7d.
metrics and operations accept --json.
Webhooks:
hubfly rt webhooks chat # ID, URL (host and path), EVENTS, ENABLED, KEY
hubfly rt webhooks create chat --url https://example.com/hooks/realtime --events channel_occupied,member_added
hubfly rt webhooks create chat --url https://example.com/hooks/realtime --events client_event --key <keyId> --disabled
hubfly rt webhooks update chat <webhookId> --events channel_occupied,channel_vacated --enable
hubfly rt webhooks test chat <webhookId> # sends a signed hubfly.test event and shows the response
hubfly rt webhooks deliveries chat --status failed --limit 20
hubfly rt webhooks redeliver chat <deliveryId>
hubfly rt webhooks delete chat <webhookId> # asks to confirm, or --yesWebhook URLs must use https:// on a public host (port 443 or 8443).
--events is checked against the event names in the managed catalog
(channel_occupied, channel_vacated, member_added, member_removed,
client_event, cache_miss, subscription_count and the hubfly.*
connection, presence and delivery events). update sends only the flags you
give. Tables show a webhook URL without its query string; --json prints the
URL as stored.
Deliveries are batched POSTs signed with the secret of the webhook's key
(--key, an app key id or public key; by default the app's newest active key):
X-Pusher-Key and X-Pusher-Signature (HMAC-SHA256 hex of the body, so the
webhook() validators of Pusher server libraries work), plus
X-HubFly-Delivery-Id (dedupe on it: delivery is at least once) and
X-HubFly-Signature: t=<unix seconds>,v1=<HMAC-SHA256 hex of "<t>.<body>">
(reject old t values to stop replays). Failed deliveries are retried with
backoff for up to 24 hours, then marked dead; the delivery log keeps 7 days
and any delivery can be queued again with redeliver. test is not written to
the delivery log; it exits non-zero when the receiver does not answer 2xx.
Live state, history and usage:
hubfly rt channels chat --prefix presence- # occupied channels: NAME, TYPE, SUBSCRIBERS, USERS
hubfly rt channels inspect chat presence-room --members 50
hubfly rt history chat --channel orders --limit 20 # OFFSET, AT, EVENT, SIZE, DATA
hubfly rt history purge chat --channel orders # without --channel: every channel of the app
hubfly rt connections chat --user u_42 # SOCKET ID, APP, USER, CHANNELS, PROTOCOL, LIBRARY, CONNECTED
hubfly rt disconnect chat --connection 1234567890.1234567890
hubfly rt disconnect chat --connection 1234567890.1234567890 --mode disconnect
hubfly rt disconnect chat --user u_42 # every connection and session of the user
hubfly rt usage chat --since 7d # or --since 2026-10-01; default: the last 30 dayshistory reads the messages the app retains (--history memory|durable on
the app). Messages of private-encrypted- channels and hubfly.e2e envelopes
stay opaque: HubFly cannot read them, so the table shows them as end-to-end
encrypted (decrypt them on your own server with the HubFly server SDK).
connections lists every app of the server; its --user and --channel
filters need an app when the server has several (--app).
disconnect uses --mode terminate by default: the connection closes with
code 4009, the client does not reconnect and its session is discarded.
--mode disconnect closes with code 4200: the client reconnects at once and
HubFly SDK clients resume their session. --user always terminates. Like
send, disconnect strips the client key from end-to-end connection ids
(<socket_id>~<key>) before any request, and connections never prints one.
history purge, disconnect and webhooks delete ask for confirmation; pass
--yes in scripts.
usage prints daily totals per app (UTC days, kept 400 days): peak
connections, connection minutes, messages in and out, bytes in and out and
webhook deliveries.
hubfly db (aliases database and databases) manages PostgreSQL, MySQL and
SQLite/libSQL instances. Use an instance id or name, with --project when names
are ambiguous. HubFly chooses the region. Run hubfly db engines for the
available engine versions, size presets and current prices.
hubfly db engines
hubfly db create app-db --engine postgresql --size nano --storage 10 --project new
hubfly db list
hubfly db info app-db --json
hubfly db url app-db # password hidden
hubfly db env app-db --reveal # treat the output as a secret
hubfly db credentials app-db --reveal
hubfly db credentials rotate app-db --yes
hubfly db connect app-db -- --version # local psql or mysql client
hubfly db shell app-db # remote console, no local client needed
hubfly db bind app-db --to web --new-user --access read_write
hubfly db bind list app-db
hubfly db bind remove app-db <bindingId> --yesCreation shows a price quote and asks for confirmation; scripts must pass
--yes. Pick an existing managed project with --project <id|name>, or create
one with --project new [--project-name <name>]. MySQL defaults to the smallest
preset with at least 1 GiB of RAM. The default database is app (override with
--database for PostgreSQL/MySQL); the admin username is fixed to admin.
--no-wait returns when creation is accepted; --timeout limits the wait.
Credentials stay hidden unless you use --reveal or create/rotate them.
connect passes passwords through the client's environment, and any client
arguments follow --. libSQL uses its HTTPS-compatible URL and an auth token;
use a libSQL SDK or hubfly db query instead of a PostgreSQL/MySQL client.
Bindings write DATABASE_URL, or LIBSQL_URL and LIBSQL_AUTH_TOKEN, into a
Cell project's environment. Deploy the app to pick up the new values.
--new-user creates a dedicated PostgreSQL/MySQL user for the binding.
hubfly db stop app-db
hubfly db start app-db
hubfly db restart app-db
hubfly db resize app-db --size small --storage 20 --restart --yes
hubfly db upgrade app-db --yes
hubfly db rename app-db orders-db
hubfly db protect orders-db on
hubfly db network orders-db
hubfly db network allow orders-db --my-ip --label office
hubfly db network remove orders-db 203.0.113.10/32
hubfly db network public orders-db off --yes
hubfly db protect orders-db off
hubfly db delete orders-db --yes --waitStorage can only grow. Resize shows the old and new prices; some changes need a
restart (--restart applies it now). Stopped instances pay for storage only.
Deletion requires the instance name or --yes, and protection must be off.
Public access is on by default; --allow or --allow-my-ip at creation sets an
IP allowlist. --private, or disabling public access later, removes the public
endpoint, including access by bound apps that use that hostname. TLS is always
required when connecting to a public endpoint.
Run SQL:
hubfly db query mydb "SELECT id, email FROM users LIMIT 5"
hubfly db query mydb --file ./report.sql --format csv > report.csv
cat migrate.sql | hubfly db query mydb --write
hubfly db query mydb "SELECT * FROM orders" --database shop --max-rows 2000 --format jsonQueries run read-only unless you pass --write; a read-only run that tries to
change data fails and says so. The SQL comes from the argument, --file <path|->
or stdin (up to 100 KB; use hubfly db import for larger scripts). Each statement
prints a table (NULL for null values) or its command tag; results are capped at
--max-rows (default 500, at most 5000) and --timeout (default 30s, at most 60s).
--format csv prints a single result set. Notices and errors (code, position, hint)
go to stderr, and the command exits non-zero when a statement fails.
Logical databases, users and access (PostgreSQL and MySQL):
hubfly db databases mydb # default and system databases are marked
hubfly db databases create mydb shop --owner app_user
hubfly db databases delete mydb shop --force # asks you to type the name; --force disconnects sessions first
hubfly db users mydb
hubfly db users create mydb reporter --preset read_only --databases shop
hubfly db users update mydb reporter --preset read_write --all-databases
hubfly db users rotate mydb reporter
hubfly db users delete mydb reporter --reassign-to adminPresets: read_only, read_write (the default; can also run migrations) and
admin (all databases; never a superuser). users create and users rotate
print the password once (with --json, in password). The primary admin user
cannot be deleted; rotate its password with hubfly db credentials rotate.
Access tokens (SQLite/libSQL) and extensions (PostgreSQL):
hubfly db tokens create mylite ci --access read_only --expires 30 # prints the token once
hubfly db tokens mylite
hubfly db tokens revoke mylite <tokenId>
hubfly db extensions mydb --database shop
hubfly db extensions install mydb pgcrypto --database shop
hubfly db extensions remove mydb pgcrypto --cascadeNew tokens have full access unless you pass --access read_only. libSQL cannot
revoke a single token: tokens revoke rotates the signing key, which invalidates
every token of the instance (apps connected with hubfly db bind get a new token).
Export and import:
hubfly db export mydb --database shop # waits, then downloads to the current directory
hubfly db export mydb --format sql --output shop.sql
hubfly db export mydb --no-wait # prints the export id
hubfly db exports mydb
hubfly db exports download mydb <exportId> --output shop.dump
hubfly db import mydb ./shop.dump --database shopPostgreSQL exports default to the native format (pg_dump -Fc); --format sql
gives plain SQL. MySQL and libSQL export gzip-compressed SQL. Exports are kept for
7 days, and the CLI never overwrites an existing file. import asks for
confirmation, uploads the file (up to 5 GB) and runs it against the existing data;
it stops at the first error. The format comes from the file name: .dump and
.backup are PostgreSQL native dumps, .sql and .sql.gz are SQL; pass
--format for other names. There are no automatic backups yet, so export before
risky changes.
Certificates, sessions, query insights and configuration:
hubfly db cert mydb --output mydb-ca.pem # CA for sslmode=verify-full / --ssl-mode=VERIFY_IDENTITY
hubfly db sessions mydb
hubfly db sessions cancel mydb <sessionId> # cancels the running query
hubfly db sessions terminate mydb <sessionId> # closes the connection
hubfly db insights mydb --order-by mean_time --limit 10
hubfly db insights reset mydb
hubfly db config mydb # --all for every setting
hubfly db config set mydb work_mem=16MB max_connections=200 --restart later --note "more memory"
hubfly db config set mydb work_mem= # back to the default
hubfly db config history mydb
hubfly db config revert mydb 3
hubfly db logs mydb --follow
hubfly db metrics mydb --window 24h
hubfly db operations mydbconfig set validates the change, shows the diff and which settings need a
restart, and asks before applying. With --restart later (the default) those
settings wait for the next hubfly db restart; --restart now restarts the
database right away. Sessions opened by HubFly (console, monitoring) are marked
and cannot be cancelled. logs needs the database operate permission. Engine
features are checked first: tokens are libSQL-only, extensions PostgreSQL-only,
and libSQL has no users, sessions, insights or configuration.
hubfly storage manages HubFly Object Storage: S3-compatible buckets, objects,
scoped access keys, published assets and video encoding. Storage projects are
regional: you pick a region that offers Object Storage when you create one.
<project> is a Storage project id or name; --project may be omitted when you
have exactly one Storage project, or set HUBFLY_STORAGE_PROJECT. Object paths
are <bucket>/<key>. Commands that print data accept --json.
hubfly storage list
hubfly storage create media --region <id|name> # asks to confirm unless --yes, then waits until ready
hubfly storage info media # S3 endpoint, state, usage, buckets
hubfly storage usage media # used/quota, egress and encoding per day (30 days)
hubfly storage delete media # type the name to confirm, or --yesDeleting a Storage project revokes its access keys at once; its buckets and objects are removed after the region's recovery window.
Buckets and objects:
hubfly storage buckets create photos --versioning enabled
hubfly storage buckets update photos --versioning suspended --image-retention 30d
hubfly storage buckets delete photos # only empty buckets
hubfly storage ls # buckets
hubfly storage ls photos/2026/ # "directories" (PRE) and objects
hubfly storage ls photos --recursive --limit 0 # every key
hubfly storage put ./cat.jpg photos/ # key photos/cat.jpg
hubfly storage put ./site photos/www --recursive # site/a/b.html -> www/a/b.html
hubfly storage get photos/cat.jpg ./cat.jpg # --force overwrites; "-" writes to stdout
hubfly storage rm photos/old/ --recursive # lists, counts and asks to confirm (or --yes)
hubfly storage url photos/cat.jpg # temporary download link (15 minutes)put streams each file to a short-lived signed upload URL. The content type
comes from --content-type, then the file extension, then the file's first
bytes. One upload is limited to 20 GB; for larger files use an S3 client with
multipart upload and a key from keys create. Versioning can be enabled or
suspended but not turned off again; image retention (keep or 1h to 365d)
controls how long generated image variants are cached.
S3 access keys:
hubfly storage keys
hubfly storage keys create backend # read/write on every bucket
hubfly storage keys create cdn --read-only --bucket photos --format dotenv
hubfly storage keys create backend --to web # writes the variables into Cell project "web"
hubfly storage keys revoke <accessKey>The secret is printed once, when the key is created (with a warning on
stderr). --format dotenv|json and --to use HUBFLY_STORAGE_ENDPOINT,
HUBFLY_STORAGE_REGION, HUBFLY_STORAGE_ACCESS_KEY_ID,
HUBFLY_STORAGE_SECRET_ACCESS_KEY, HUBFLY_STORAGE_BUCKET (bucket-scoped keys
only) and the AWS SDK names AWS_ENDPOINT_URL_S3, AWS_REGION,
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY. --to stores the secrets as
secret variables, prints only the variable names, reloads the project's
containers, and revokes the new key again if the variables cannot be written.
HubFly Storage accepts any SigV4 signing region (the CLI uses us-east-1); use
path-style addressing. --debug never prints secrets.
Assets and video:
hubfly storage publish photos/cat.jpg # public asset; prints page and file URLs
hubfly storage publish photos/report.pdf --protected # prints the asset access key
hubfly storage share photos/clip.mp4 --expires 2h # temporary link (5m to 24h)
hubfly storage assets --bucket photos
hubfly storage unpublish photos/cat.jpg # deletes the asset AND the object
hubfly storage encode photos/clip.mp4 --max-height 1080 --wait
hubfly storage media # encoding jobsA published asset's page URL shows a viewer; add /media for the file itself.
Protected assets need ?key=<access key> (or the X-Asset-Key header) or a
share link. unpublish removes the asset together with its source object,
renditions and image variants, so download the object first if you want to keep
it. encode produces HLS renditions up to the chosen height (default 720);
--wait follows the job and prints a signed player URL.
Use PowerShell or Windows Terminal. Auth uses Windows Credential Manager when
available; local configuration and the downloaded inspector live under
%USERPROFILE%\.hubfly.
hubfly build validate, hubfly build explain, and automatic build detection
use the Windows hubfly-builder.exe offline inspector. It detects runtimes and
generates Linux Dockerfiles. hubfly deploy and stack image builds require
Docker with Linux containers enabled (for example, Docker Desktop). The
regional Hubcell build server remains on Linux. Box SSH connections require
ssh.exe on PATH.
hubfly update replaces the CLI executable on Windows. Reopen the command to
use the new version. Container terminal sessions support live window resizing.
$env:HUBFLY_API_URL = 'http://127.0.0.1:3000'
hubfly whoamiBy default the CLI talks to:
https://api.hubfly.spaceOverride it for local or staging environments:
HUBFLY_API_URL=http://127.0.0.1:3000 hubfly whoamiFor CI, set HUBFLY_TOKEN instead of storing a local session:
HUBFLY_TOKEN=hf_... hubfly deploy --project my-api --region eu-1 --yesAPI errors include the backend trace ID when the dashboard returns one, for example:
Authentication required (status 401, trace err_...)
Use that trace ID to find the matching backend log.
↑/↓orj/k: moveenter: select/confirmesc: backq: quit from top-level- Type text in filterable lists to search
Multi-tunnel selection:
space: toggle tunnela: toggle allenter: continue
hubfly version shows:
- version tag
- commit SHA
- build date
- OS/arch
hubfly update --check checks latest release.
hubfly update downloads the latest release for your OS/arch and replaces the local binary (Linux/macOS).
hubfly deploy works directly from your project directory. The CLI:
- creates or reuses
hubfly.build.json - checks for the latest
hubfly-builderrelease before deploy unless--builder-versionpins a specific version - runs builder inspection for auto-detected stacks
- builds the Docker image locally on your machine
- uploads a compressed image archive to the regional builder with retryable chunk uploads
- reports local build/upload failures back to the authenticated CLI deploy session endpoint
- waits for the deploy by default, or returns early with
--detach
Common examples:
hubfly deploy
hubfly deploy advanced
hubfly deploy --project new --region rw-kigali-1 --yes
hubfly deploy --project my-api --dockerfile ./deploy/Dockerfile --yes
hubfly deploy --config ./ops/hubfly.build.json --builder-version v1.7.1 --yesImportant flags:
--project: existing project id/name, ornew--region: required for non-interactive new-project deploys--yes: required in non-interactive/scripted deploys after reviewing the diff--config: customhubfly.build.jsonpath or project directory--detach: stop after upload and let Hubfly finish the deploy asynchronously--dockerfile: force Dockerfile mode for this run--builder-version: pin a specifichubfly-builderrelease
Before deploy, the CLI shows a diff for:
- image source
- resources
- ports
- volumes
- runtime environment variables
- healthcheck
- bound-container replacement behavior
Non-interactive deploys should provide enough information up front:
hubfly deploy --project my-api --region eu-1 --config ./hubfly.build.json --yesUse --detach when a script only needs to upload the image and let Hubfly finish deployment in the background.
hubfly deploy does not require any manual setup, but the build helpers are useful when you want to inspect or adjust the config explicitly.
hubfly build init
hubfly build validate
hubfly build edit
hubfly build explainWhat they do:
hubfly build init: create or refreshhubfly.build.jsonhubfly build validate: resolve the effective build plan and verify Dockerfile/builder inputshubfly build edit: openhubfly.build.jsonin$EDITORhubfly build explain: show whathubfly-builderdetected and why
Useful JSON output:
hubfly build validate --json
hubfly build explain --jsonThose commands are meant for CI and for comparing the local build plan against what the dashboard will deploy.
Enable debug:
hubfly --debug projects
# or
HUBFLY_DEBUG=1 hubfly projectsWhen debug mode is enabled:
- Non-TUI commands print debug lines to stderr.
- During TUI mode (
projects), debug lines are written to:~/.hubfly/logs/debug.log
Debug output includes:
- HTTP method/URL
- masked Authorization token
- request/response payloads (secrets, tokens and secret environment values are masked)
- tunnel route selection details
- backend error/request trace IDs when the API returns them
Runtime logs:
hubfly logs <containerIdOrName>
hubfly logs <containerIdOrName> --follow
hubfly stack logs api --followRemote command execution:
hubfly ssh <containerIdOrName>
hubfly ssh <containerIdOrName> -- ls -la /app
hubfly exec <containerIdOrName> -- printenv
hubfly exec <containerIdOrName> -- sh -lc "ls -la /app"
hubfly stack exec api -- printenv
hubfly stack ssh apiIn an interactive hubfly ssh, stack ssh, or database shell session, press Ctrl+] to detach and return to your local shell without sending exit to the remote shell.
hubfly stack reads a local Compose-style file, builds any build: services on the user machine, pushes them through the authenticated regional registry upload flow, and creates or updates each service as a normal Hubfly container.
Supported today:
imageandbuildservices- named volumes
environmentandenv_fileports,depends_on,healthcheck, andrestartx-hubflyoverrides for tier, resources, runtime, and volume size
Current behavior:
- build-backed services redeploy in place through the existing CLI deploy session path
- image-only services are recreated when their config changes
- bind mounts are ignored with warnings; use named volumes instead
Examples:
hubfly stack plan --file docker-compose.yml
hubfly stack up --project new --region eu-1 --yes
hubfly stack status
hubfly stack logs web --follow
hubfly stack down --volumes --yesFor tunnel connections, the CLI now uses a Hubfly-managed known_hosts file to avoid interactive host-key failures:
UserKnownHostsFile=~/.hubfly/known_hostsHostKeyAlias=hubfly-<tunnelId>StrictHostKeyChecking=accept-newBatchMode=yes
This prevents global ~/.ssh/known_hosts conflicts and avoids prompt-based failures in TUI sessions.
hubfly service
hubfly service --port 5600Endpoints:
GET /healthPOST /startPOST /stopGET /status
This is useful when a desktop app, editor extension, or local automation needs to manage Hubfly tunnels without controlling the interactive TUI.
This repository includes a standalone docs page:
index.html
It uses Tailwind through the CDN and can be shipped directly with GitHub Pages from the repository root.
Linux/macOS:
git clone https://github.com/hubfly-space/hubfly-cli.git
cd hubfly-cli
go build -o hubfly .
./hubfly versionWindows (PowerShell, with Go installed):
git clone https://github.com/hubfly-space/hubfly-cli.git
cd hubfly-cli
go build -o hubfly.exe .
.\hubfly.exe versionGitHub Actions workflow builds and publishes release assets on tag push (v*):
hubfly_linux_amd64.tar.gzhubfly_linux_arm64.tar.gzhubfly_darwin_amd64.tar.gzhubfly_darwin_arm64.tar.gzhubfly_windows_amd64.ziphubfly_windows_arm64.zip
Each release asset also has a .sha256 checksum file.
- Token:
~/.hubfly/config.json - Keys:
~/.hubfly/keys - Known hosts (Hubfly-managed):
~/.hubfly/known_hosts - Debug logs:
~/.hubfly/logs/debug.log
go build ./...
go test ./...