Skip to main content
awewarm-hub

awewarm-hub: Multi-tenant Hub Server aweskill companion

One always-on box keeps a whole team's subscription windows warm.

The multi-tenant hub server for awewarm: many users, one server, one-time invites — each user's secrets staying on their own machine.

English · 简体中文

Ko-fi

Version Python License

Status pip install Platform PyPI downloads GitHub stars

Real case: a five-person team on one $5 VPS. Everyone's subscription plans stay warm around the clock on the box that never sleeps — while every API key stays on each member's own laptop.

awewarm-hub splits the roles in two:

  • Operator (this machine, the 24/7 box): awewarm-hub serve + the admin commands below. You mint one-time invite codes and hand them out.
  • Users (their own machines, plain open-source awewarm only): awewarm remote connect <url> --invite awi_..., then awewarm config set <id> --remote. They never need this package.

Quick Start

1. Install and use awewarm-hub

Working in Claude Code, Codex, or another coding agent? Tell it:

Read https://github.com/wehuman01/awewarm-hub/blob/main/README.ai.md and follow it to set up your awewarm-hub server.

The agent installs the CLI, checks status and tenants read-only, and mints invite codes on your request. The resident serve process itself stays in your terminal (or systemd) — an agent never backgrounds it.

Manual install

Requires Python ≥ 3.9:

pip install awewarm-hub          # brings the awewarm engine with it
awewarm-hub -v

2. Start the server in your terminal

awewarm-hub serve is a resident process — the one thing the agent will not run for you. It belongs in your terminal, tmux, or a systemd unit (the unit and the cloudflared tunnel setup are in awewarm and awewarm-hub below):

awewarm-hub serve                # listens on 127.0.0.1:8790, data at ~/.awewarm-server
awewarm-hub invite --name alice  # prints awi_... (one use, 7 d)
awewarm-hub status               # capacity, invite counts, tenants, serve liveness

3. Manage the hub through natural language

Once the server is up, day-to-day operation goes through your agent (the full command reference is in Commands):

Invite a user

You can tell your agent:

Invite bob to the hub.

The agent mints a one-time code (awi_..., printed once) — hand it to that person promptly and privately. On their own machine, with plain open-source awewarm (they never need this package), they pair and delegate:

awewarm remote connect https://warm.example.com --invite awi_...
awewarm config set <id> --remote
Equivalent CLI commands
awewarm-hub invite --name bob                                # one invite per person
awewarm-hub invite --name team --count 5 --expires-in 7d     # batch for a team (codes share name, expiry, machine cap)
awewarm-hub status                                           # tenants against the max

Check who joined and how much they use

You can tell your agent:

Who has joined, and how much are they using?
Equivalent CLI commands
awewarm-hub status                 # capacity, invite counts, serve liveness
awewarm-hub list users             # tenants: health, usage, machines, joining code
awewarm-hub list invites           # every minted code and its fate

Suspend and restore a tenant

You can tell your agent:

Suspend alice's tenant while she's on vacation, and restore it when she's back.

Revocation is suspension, not deletion — restore undoes it, and machine pairings are untouched. The destructive --delete variant runs only when you explicitly ask to delete outright.

Equivalent CLI commands
awewarm-hub invite revoke <id|code>     # pending stops pairing, used suspends its tenant (reversible)
awewarm-hub invite restore <id|code>    # undo
awewarm-hub list invites                # find the id — the ID column, unique by construction

Beyond the basics: the two-package role split, the full command reference, tenant workspaces, and the security model are covered below — start with awewarm and awewarm-hub.

awewarm and awewarm-hub

Two packages, two roles, same MPL-2.0 license and the same wehuman01 org:

Package Who installs it What it does
awewarm everyone (each user's own machine) schedules warm-up requests; awewarm serve covers one solo server
awewarm-hub (this package) the operator only (the 24/7 box) multi-tenant serving: tenants, one-time invites, quotas, revoke/restore

The engine underneath (WarmServer, schedule, transport, the HTTP handler core) comes from the awewarm pip dependency, pinned to its minor version so the wire protocol stays in lockstep with the open-source client. Expose the hub through a cloudflared tunnel (free TLS, no open inbound ports, your origin IP stays hidden) exactly like a solo awewarm server — see awewarm's README → Remote Server. A systemd user unit is the same shape, with ExecStart=awewarm-hub serve:

[Unit]
Description=awewarm-hub serve
After=network-online.target

[Service]
ExecStart=awewarm-hub serve
Restart=on-failure

[Install]
WantedBy=default.target

systemctl --user enable --now awewarm-hub (with loginctl enable-linger $USER on headless boxes).

Commands

awewarm-hub serve [--data-dir/--bind/--port]   # the resident hub server
                 [--max-tenants/--max-conns-per-tenant/--max-machines/--tick-seconds]
awewarm-hub status [--details]                 # capacity, invite counts, tenants, serve liveness
awewarm-hub invite [--name <who>] [--count N] [--expires-in 30m|12h|7d] [--machines N]  # mint (bare form)
awewarm-hub list users [--api|--reveal|--json] # tenants: health, usage, machines, joining code
awewarm-hub list invites [--reveal|--token|--json]  # every minted code: pending/used/revoked/expired, its machine cap, its tenant's token (--token)
awewarm-hub invite rename <id|code> <name>     # relabel an invite's note; a used one's tenant follows
awewarm-hub invite revoke <id|code>            # kill an invite: pending stops pairing, used suspends its tenant (reversible)
awewarm-hub invite revoke <id|code> --delete   # wipe the ledger row outright; a used one takes its tenant (irreversible)
awewarm-hub invite restore <id|code>           # undo a revoke
awewarm-hub invite extend <id|code> --expires-in 7d  # push an expiry out from now; an expired code pairs again
awewarm-hub config [--data-dir /data|--unset]  # default data dir for this machine
awewarm-hub config --max-tenants 20 [--max-conns-per-tenant N] [--max-machines N] [--reset]
                                               # capacity caps; a running serve adopts them without a restart
awewarm-hub config --persist-keys on|off       # may tenants store API keys on this box?
                                               #   off by default; off purges every stored key at once
awewarm-hub self-update [--check]              # upgrade from PyPI

The four invite subcommands take the invite's id (the ID column list invites prints — unique by construction), the full code, or the masked form (awi_F3XW…) when it identifies one invite; an ambiguous prefix errors with the candidates. Pairing still demands the full code, so neither an id nor a prefix is a secret, and day-to-day operations never need the full code.

How It Works

Each tenant gets a private workspace under tenants/<id>/ (connections, state, RAM keyring — invisible across tenants). tenants.json keeps invite codes and tenant tokens in the clear so the operator can recover either one already sent (list invites --reveal / --token); authentication compares a token's SHA-256 hash, so pairings survive a restart. A user who lost their token is handed it back and reconnects with awewarm remote connect <url> --token <it> — same tenant, same connections. Anyone who can read the data dir can spend a pending invite or act as a tenant, so guard it. The invite code is the one ledger of authorization: revoke awi_... kills a pending code or suspends the tenant that used it (token rejected, connections stop ticking, capacity slot freed), and restore awi_... undoes either — machine pairings are untouched, so the round-trip is lossless. revoke awi_... --delete wipes the row from the ledger instead: no revoked tombstone stays, a used one takes its tenant with it (token dead, capacity slot freed, workspace kept on disk), and restore cannot bring any of it back; it also accepts an already-revoked row — the purge-a-tombstone case. A machine cap is stamped into each invite at minting (invite --machines N, defaulting to the max-machines cap); to give an online user more machines, raise the machines value on their invite row in tenants.json (a running serve adopts the edit) or hand them a fresh code. The three capacity caps live in the registry's serve record: serve flags stamp them at launch, and awewarm-hub config --max-tenants 20 retunes them at any time — a running serve adopts the new values without a restart (every tenant action and each tick re-read the record). A light per-tenant rate limit (60 requests/minute) stops a looping client.

One trust rule, stated plainly: the hub fires requests with its users' API keys, so their plaintext keys pass through its RAM. Hub for people who trust the machine's operator (and root); a shared VPS with strangers is not that.

API keys never touch disk by default — they live in server RAM and are re-pushed by each user's machine after a restart. Invite codes and tenant tokens are the on-disk exceptions above, kept recoverable on purpose; guard the data dir.

One more opt-in exception exists for users whose machines are rarely online: with awewarm-hub config --persist-keys on (default off, warns when enabled) a user may confirm per connection (awewarm config set <id> --persist-key on) that their key is stored in their workspace's keys.json (plaintext, 0600), so a hub restart keeps their warm-ups ticking with no re-push. Both sides can withdraw at any moment: the user turns the flag off (the hub deletes the key), the operator flips the switch off (every stored key on the box is purged at once), and revoking or deleting an invite purges that tenant's keys — the workspace otherwise stays, as always. The feature is discouraged by design; leaving everything off is the recommended posture.

Security

Short version for users: every ordinary leakage path is closed by design; the one thing that remains is trusting the hub's machine.

  • Account logins never leave your machine. The hub only accepts API-key (subscription) connections; CLI-account (OAuth) credentials are rejected at the wire and live on wherever you logged in. There is no username, password, or session cookie to leak.
  • Your API key exists on the hub in RAM only (unless you explicitly opt into --persist-key — discouraged; then it sits in your workspace's keys.json in plaintext, 0600, and disappears again the moment either you or the operator withdraws). Never logged (activation results are scrubbed of keys and auth headers), and no endpoint reads one back — /v1/state reports only whether a key is present. Traffic rides the operator's HTTPS tunnel end to end.
  • A stolen tenant token is not a stolen API key. With your awt_... an attacker can manage and trigger your connections, but cannot read a stored key and cannot send it to their own server — replacing a connection always pushes a fresh key that overwrites the old one. If a token leaks: have the operator revoke your invite (the token dies at once), re-pair with a fresh code; your machine re-pushes everything.
  • Tenants are invisible to each other — private workspaces, constant-time token-hash comparison, a 60 req/min per-tenant rate limit.

The one boundary, stated plainly: the hub fires warm-ups with your key, so its plaintext passes through the hub process's memory. Whoever operates the hub — or holds root on its box — can read it. Everything above is enforced by design; this last item is a trust decision, the same rule stated under How It Works: a hub is exactly as trustworthy as the machine it runs on.

Recommendations:

  • Users: delegate a dedicated, revocable key — not the one the rest of your tooling lives on — and rotate it whenever in doubt.
  • Users: connect via the hub's https:// URL only, never a bare http://host:port, and keep the client package current.
  • Operators: expose the hub only through the cloudflared tunnel (or an equivalent TLS proxy), keep the default --bind 127.0.0.1, guard the data dir (tenants.json holds invites and tenant tokens in the clear, by design), and keep the package updated.

Upgrading from pre-split awewarm serve --hub

The data dir (~/.awewarm-server, or whatever --data-dir/the old hub config --data-dir set) carries over unchanged — tenants, invites, and the persisted data-dir setting all keep working. Stop the old serve, install this package, start awewarm-hub serve. The old spellings (awewarm serve --hub, awewarm hub ...) die with a tombstone in awewarm naming their replacement here.

Upgrading to v0.5.6 (breaking): revoke/restore now address invite codes only — revoke t_... is gone; find the code a tenant joined with via list invites --reveal. The first launch after upgrading migrates tenants.json once: tenant-level suspensions move onto their invite's revokedAt, and any invite row minted before codes were stored on disk is dropped together with the tenant it produced (its token dies; its workspace stays — re-pair with a fresh invite). Upgrade the package and restart serve together, so both processes speak registry v2.

Config

awewarm-hub config is the settings entry — one command, two concerns. Alone it shows what is in effect and where it comes from: the data dir and the three capacity caps. --data-dir /data persists the default data dir on the hub machine (--unset clears it); when given with cap flags it instead selects the registry they apply to, once, like the --data-dir flag on every other command. The default is ~/.awewarm-server, shared with awewarm's solo server. Inside it: tenants.json (token hashes, invite codes, the serve record with caps/bind/version/start time) and one private tenants/<id>/ workspace per tenant.

The caps — --max-tenants, --max-conns-per-tenant, --max-machines (--reset clears them back to the 10/5/1 defaults) — live in that serve record, the one place every process reads: serve flags stamp them at launch, config retunes them at any time, and a running serve adopts the new values without a restart (every tenant action and each scheduling tick re-read the record; one-shot CLI processes read the same numbers, even before a first launch).

Self-Update

awewarm-hub self-update            # upgrade to the latest release
awewarm-hub self-update --check    # show versions only

Development

python3 -m venv .venv && source .venv/bin/activate
pip install -e ../awewarm -e .     # the open-source engine, editable, plus this package
python3 -m unittest discover -s tests

awewarm-hub -v says editable (with the git state) when running from this checkout; pip's recorded metadata freezes at pip install -e . time, so re-run it after a version bump to keep pip show in sync. awewarm-hub self-update refuses on a checkout — pull and re-install instead.

The source repository is wehuman01/awewarm-hub (open, MPL-2.0); releases are built and published to PyPI on tag push. See docs/CONTRIBUTING.md for the engineering doctrine and docs/CHANGELOG.md for release history.

Support

If awewarm-hub keeps your team's quotas warm, consider supporting it:

  • ⭐ Star the repo — it helps others find it.
  • ☕ Ko-fi — buy me a coffee.
  • 💬 WeChat — scan the QR code below.

WeChat Pay

awewarm-hub is free and open source. Sponsors keep it maintained — thank you.

Awesome Ecosystem

awewarm-hub is part of a growing family of "awesome" tools — CLI-first, local-first, and operable by AI agents.

CLI Tools

  • aweskill — CLI-first skill package manager supporting 47+ AI coding agents.
  • aweswitch — Agent profile switcher for Claude Code, Codex, and OpenCode.
  • awerouter — Smart router that splits requests between Flash and Pro models using structural signals, cutting unnecessary model spend.
  • aweshelf — Bookmark, categorize, and restore AI coding sessions; pairs with aweswitch to save profiles and launch with one command.
  • aweshare — Share local Ollama/vLLM backends, domestic coding plans, or authorized OpenAI/Anthropic subscriptions through a self-hosted hub — a sharing economy for tokens.
  • awewarm — Subscription window warmer that keeps AI coding-plan windows active, for local setups and through a remote hub server.
  • awescholar — AI-agent-operable scientific literature discovery and curation.

Desktop Apps

  • awedot — A floating orb at your screen edge keeps track of the current AI session: bookmark it in one click, resume anytime, and pair with aweswitch to pin the agent's config (e.g., relaunch with the GLM model).

Project Collections

  • Awesome AI Meets Biology — A curated survey of AI applications in biology, bioinformatics, and biomedical research. Powered by awescholar.
  • Awesome AI Virtual Tumor — A curated collection of state-of-the-art AI systems for virtual tumor modeling and simulation: static models, dynamic models, agents, benchmarks, and reviews.

Metadata

Release files for awewarm-hub 0.7.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for awewarm-hub 0.7.1
File Size Uploaded
awewarm_hub-0.7.1.tar.gz 82.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for awewarm-hub 0.7.1
File Interpreter ABI Platform
awewarm_hub-0.7.1-py3-none-any.whl Python 3 none any Details

Total release size: 127.5 kB

Release files / awewarm_hub-0.7.1.tar.gz

Download URL awewarm_hub-0.7.1.tar.gz
Size 82.2 kB
Tags Source
SHA-256 checksum
How to use checksums
5be0db666e29f9a821c904d30f56239e3cde024c05469d790333721d2528bef8
BLAKE2b-256 checksum
How to use checksums
106f5cd62309811d4c2c58b742d3b87b10d941a93e67c7f62d06f0a412e2b6e0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release files / awewarm_hub-0.7.1-py3-none-any.whl

Download URL awewarm_hub-0.7.1-py3-none-any.whl
Size 45.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
75c825a678cb56b40a9269fa07281cf23aac0a543163da520617309d0a043b77
BLAKE2b-256 checksum
How to use checksums
1dd84e7ebf45d80d3aec3732a559b3016b46e25d5ef2cef1acc37b58cc10a82f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release history Release notifications | RSS feed

0.7.2

2 release files

This release

0.7.1 This release

2 release files

0.7.0

2 release files

0.6.2

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.8

2 release files

0.5.7

2 release files

0.5.6

2 release files

0.5.5

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page