npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

aweshare

v0.7.6

Published

Open-source, local-first AI capability relay

Readme

Producers run a lightweight agent on their own machine and share local Ollama/vLLM or authorized OpenAI/Anthropic backends. Upstream API keys live only on the producer's device and are injected by the local agent at forwarding time. Consumers point a standard OpenAI/Anthropic SDK at the hub and call models by namespace/alias — exactly like using any other model vendor.

Consumer (standard SDK, zero changes)         Producer side
┌───────────────────────┐            ┌────────────────────────────┐
│ Claude Code           │            │ aweshare producer (Node CLI)   │
│  ANTHROPIC_BASE_URL ──┼──► HTTPS ──┤  ~/.aweshare/config.toml   │
│ OpenAI SDK / Codex    │            │  ~/.aweshare/secrets.json  │
└───────────────────────┘            │   │ upstream key injection  │
           │ /v1/messages            │   ▼ (the only place it happens)
           │ /v1/chat/completions    │  Ollama / vLLM / OpenAI / Anthropic
           ▼                         │
┌─────────────────────────────┐      │
│ aweshare hub (public, 1 node)│◄───── WSS reverse tunnel (agent dials out)
│ auth / route / meter         │      no public IP, no port forwarding needed
└─────────────────────────────┘
  • Invite-only trust: admission runs through one-time invite codes the operator mints; every admitted consumer may call every offering. No payments, no marketplace.
  • Namespaced aliases: peng/gpt-4o is globally unique with one owner — routing is a deterministic lookup.
  • v1 relays native transparent SSE for OpenAI↔OpenAI (chat completions and Responses), Anthropic↔Anthropic. No cross-protocol conversion, no smart routing, no web console.

Trust boundary (read this first)

  • To route and meter, consumer prompts and model responses transit the hub in plaintext — this is not end-to-end encryption. The hub persists no request/response content, but the hub operator can technically see it. Only use a hub instance you trust — which is why the hub is open source and self-hostable.
  • Upstream API keys never leave the producer's device and are never sent to consumers. Tokens are stored twice on purpose: a peppered SHA-256 hash drives authentication, and the plaintext is kept so the operator can hand a lost one back (hub list invites --token). Invite codes work the same way — their plaintext is re-viewable with hub list invites --reveal. A DB leak therefore exposes every identity, so guard the data dir.
  • Token revocation is reversible suspension (hub admin invite revoke N / hub admin invite restore N, by invite), and an invite and the producer it minted move together: revoking a redeemed code suspends that producer (and closes its tunnel), and restoring from either handle revives both. Nothing is deleted on revoke — offerings and usage history survive a suspension.

Compliance and disclaimer

  • aweshare is relay software: it cannot and does not judge whether you are allowed to share a given upstream key or subscription — that question is between you and the upstream provider. Being able to call an API yourself does not mean you may resell or re-provide it to third parties.
  • Before sharing anything, read the upstream's terms (account rules, subscription and seat limits, forwarding, commercial-use clauses). Sharing a personal-subscription key — coding plans included — with third parties likely violates those terms; self-hosted open models have no such issue. When in doubt, don't share.
  • Sharing a CLI login (login = "codex") raises the stakes further: the credential is account-wide — it unlocks every subscription under that login, not one scoped key — so relaying it to third parties carries a higher risk of account suspension or termination than sharing an API key. aweshare producer doctor repeats this warning; the decision and its consequences sit with the producer.
  • The producer bears the consequences of sharing (key revocation, account suspension or termination by the upstream). The hub operator is responsible for operating the hub lawfully and for informing consumers of the plaintext-transit boundary above.
  • The software is provided "as is" under the proprietary license — free to use and self-host, no redistribution — without warranty of any kind. The authors and contributors are not liable for how aweshare is used or for any damage arising from sharing access through it.

Quickstart

Published as aweshare on npm (requires Node ≥ 22) and as a Docker image (ghcr.io/wehuman01/aweshare). No clone needed.

Platform support: macOS and Linux are fully supported. Windows works for the hub, consumers and producer basics (tested in CI), with two caveats: producer start --install has no Windows equivalent (use --background instead), and producer reload is a no-op there — config changes are always picked up automatically within a couple of seconds, and stopping a background producer is immediate rather than graceful (POSIX signals like SIGHUP/SIGTERM terminate unconditionally on Windows).

Let an AI agent set it up

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

Read https://github.com/wehuman01/aweshare/blob/main/README.ai.md and follow it to install and configure aweshare.

The agent installs the CLI and the skill, asks whether you are a hub operator, producer, or consumer, and does everything that is safe to automate — editing configs, minting invites, running producer doctor. Steps that print one-time tokens (hub init, consumer join) or start long-running services (hub serve, producer start) stay in your terminal. After setup you can ask things like "share my local Ollama model as peng/qwen2.5.7b", "why is my producer offline?", or "who used my models this week?".

Manual setup

1. Start the hub (operator, one VPS)

npm (simplest):

npm install -g aweshare
aweshare hub init        # data in ~/.aweshare-hub; prints the admin token — save it
aweshare hub serve       # listens on :8787 (put Caddy/nginx TLS in front)

The hub can also host models itself — no producer machine needed. Scaffold with aweshare hub produce init, add [[backends]]/[[offerings]] sections to ~/.aweshare-hub/config.produce.toml (same format as a producer's config, alias namespace hub/…), put the upstream keys in ~/.aweshare-hub/secrets.json, and run the same aweshare hub serve — the catalog mounts automatically and consumers dial hub/<name> like any offering.

docker:

docker run -d --name aweshare-hub --restart unless-stopped \
  -p 127.0.0.1:8787:8787 -v "$PWD/data:/data" ghcr.io/wehuman01/aweshare:latest
docker exec aweshare-hub aweshare hub init   # first run: prints the admin token, save it

Then bring people in. Producers join on their own — invite codes (asi_…, single use). Two modes:

# bound: lock the code to a specific name ("inviting that user")
aweshare hub admin invite mint --name peng [--expires-in 7d]      # → asi_..., send to the producer

# unbound: batch hand-out; the producer submits name + email at redeem (stored on the hub)
aweshare hub admin invite mint --count 10 [--expires-in 7d]

# the producer redeems it themselves (no token hand-off needed):
aweshare producer join --hub https://hub.example.com --code asi_... [--name NAME --email [email protected]]

Consumers join the same way — a consumer code redeems into an asc_ key the consumer keeps:

aweshare hub admin invite mint --role consumer --name alice [--expires-in 7d]   # → asi_..., send to the consumer

# the consumer redeems it themselves (prints the asc_ token once, with the SDK
# env vars ready to paste — save the token, it will not be shown again):
aweshare consumer join --hub https://hub.example.com --code asi_...

# no aweshare installed? one curl works too:
# curl -s -X POST https://hub.example.com/invites/v1/redeem \
#   -H 'content-type: application/json' -d '{"code":"asi_..."}'

Three token roles, one per party:

| Role | Who holds it | How it's used | |---|---|---| | admin | hub operator (you only) | the admin REST API (/admin/v1/*); CLI side: hub admin invite mint / admin invite revoke\|restore\|extend / admin offering revoke\|restore / list / status | | producer (asp_...) | the agent on the producer's machine | set as token in ~/.aweshare/config.toml; the agent registers its offerings with it | | consumer (asc_...) | whoever calls the models | set in SDK env vars (ANTHROPIC_AUTH_TOKEN / OPENAI_API_KEY); it identifies the consumer for metering, limits and suspension |

The producer's name becomes their alias namespace (the peng/ in peng/gpt-4o).

The operator owns admission: one-time invite codes for both roles, the only admission path (every identity carries its invite handle for its whole lifecycle). A redeemed consumer key may call every offering on the hub — if you let someone in, they can use what is shared. Guardrails: per-consumer hub limits (rate, concurrency, token budgets), per-offering caps (maxConcurrentUsers, dailyTokens) and hub admin invite revoke suspension, all enforced by the hub.

2. Producer first run (in this order)

npm install -g aweshare   # ⓪ once, on the producer machine (Node ≥ 22)

# ① join with your invite code (writes ~/.aweshare/config.toml + secrets.json, 0600)
aweshare producer join --hub https://hub.example.com --code asi_...
#    or, with a producer token handed to you directly:
aweshare producer init --hub https://hub.example.com --token asp_...

# ② Edit the config (see below); put upstream keys in secrets.json — they never leave this machine

# ③ doctor: pre-flight checks, ordered to find the first failing link
aweshare producer doctor

# ④ Start (long-running; when it stops, aliases go offline and consumers get 503)
aweshare producer start            # foreground; add --background to detach it
#    detached runs are checked with 'aweshare producer doctor --status'
#    and stopped with 'aweshare producer stop'
#    Or install a system service: starts now, starts automatically on boot,
#    and restarts after a crash (macOS launchd / Linux systemd user service):
aweshare producer start --install
#    'producer stop' stops the current run only; boot still starts it.
#    'producer stop --purge' also removes auto-start permanently.

3. Consumer first run (in this order)

# ⓪ redeem your invite code (skip if the operator handed you an asc_ key directly)
#    prints the token once with ready-to-paste env vars — save it, it will not be shown again
aweshare consumer join --hub https://hub.example.com --code asi_...

# ① one small curl to prove the path
curl https://hub.example.com/v1/chat/completions \
  -H "Authorization: Bearer asc_..." -H "content-type: application/json" \
  -d '{"model":"peng/qwen2.5.7b","messages":[{"role":"user","content":"ping"}]}'

# ② configure your tool (below) → run one minimal task
# ③ check usage: GET /admin/v1/usage with your consumer key (you only see your own rows)
# ④ only then move to real workloads

Consumer tool configuration

Match the protocol to the tool first — an alias speaks exactly one wire protocol and the hub never translates. Base URLs come in exactly two shapes: both OpenAI wires (openai-chat / openai-responses) point at …/v1, anthropic uses the bare hub address. The PROTOCOL column of consumer list / GET /v1/catalog shows these labels:

| PROTOCOL | Endpoint | base_url | Who can use it | | --- | --- | --- | --- | | anthropic | /v1/messages | https://hub.example.com | Claude Code, the Anthropic SDK | | openai-chat | /v1/chat/completions | https://hub.example.com/v1 | any OpenAI-compatible tool/SDK — opencode, zcode and other coding agents usually dial this | | openai-responses | /v1/responses | https://hub.example.com/v1 | Codex CLI (default wire_api), opencode, Cline (OpenAI Native) — not Codex-only |

A client may speak several wires (opencode speaks both OpenAI ones); pointing a chat-completions tool at an openai-responses alias (or vice versa) is a 404 (unknown model alias … (no … offering under this alias)).

OpenAI SDK / any OpenAI-compatible tool

const client = new OpenAI({ baseURL: 'https://hub.example.com/v1', apiKey: 'asc_...' })
await client.chat.completions.create({ model: 'peng/gpt-4o', messages: [...] })

Claude Code (the key is the asc_ consumer key — not any upstream x-api-key)

export ANTHROPIC_BASE_URL=https://hub.example.com
export ANTHROPIC_API_KEY=asc_...
claude --model peng/sonnet

If Claude Code has a stale OAuth login it overrides env config — switch with /login or clean stored credentials.

Codex (must point at a responses-protocol offering — wire_api = "chat" was removed in 2026-02 and no longer works)

[model_providers.aweshare]
base_url = "https://hub.example.com/v1"

OpenCode (~/.config/opencode/opencode.json; the provider speaks both OpenAI wires — pick per the table above)

{
  "small_model": "aweshare/peng/flash-lite",
  "provider": {
    "aweshare": {
      "npm": "@ai-sdk/openai-compatible",
      "options": { "baseURL": "https://hub.example.com/v1", "apiKey": "asc_..." },
      "models": {
        "peng/qwen38": { "name": "peng/qwen38", "release_date": "2026-09-01" },
        "peng/flash-lite": { "name": "peng/flash-lite", "release_date": "2026-09-01" }
      }
    }
  }
}

Set small_model — otherwise the session-title request collides with your first message: opencode fires it concurrently, both count against the alias's maxConcurrencyPerUser, and the default 1 turns the title call into a 429 PRODUCER_MAX_CONCURRENCY retry loop. Left unset, opencode auto-picks a small model from the same provider, but only custom models carrying a recent release_date and an id/name matching its cheap keywords (flash/lite/mini/nano/haiku/small/fast) are eligible — entries without release_date read as 1970 and never qualify — and with no eligible candidate it falls back to the main alias and hits the cap anyway. Pointing small_model at any non-aweshare provider works too. Hub-side, admission queueing (see Per-offering usage caps) already absorbs the short version of this collision: the title request holds the slot for a few seconds and the main request queues through instead of erroring.

Discovering models: GET /v1/models (OpenAI SDK client.models.list()) returns every alias registered on the hub, with online status.

Producer config reference (~/.aweshare/config.toml)

hubUrl = "https://hub.example.com"
token = "asp_..."

[[backends]]
id = "ollama"
protocol = "openai"                      # openai-style baseUrl includes /v1 (SDK convention)
baseUrl = "http://127.0.0.1:11434/v1"

[[backends]]
id = "anthropic-main"
protocol = "anthropic"                   # anthropic-style baseUrl excludes /v1 (agent adds it)
baseUrl = "https://api.anthropic.com"
keyRef = "anthropic-key"                 # key lives in secrets.json under this name

[[backends]]
id = "glm-responses"
protocol = "responses"                   # responses-style baseUrl includes the version path
baseUrl = "https://open.bigmodel.cn/api/v1"
keyRef = "glm-key"                       # e.g. a GLM coding-plan key (Codex-ready)

[[backends]]
id = "codex-account"
protocol = "responses"
baseUrl = "https://chatgpt.com/backend-api/codex"
login = "codex"                          # account auth instead of a key; exclusive with keyRef
# loginHome = "~/.config/aweswitch/accounts/codex/cxo-second"  # another login's dir; default ~/.codex
# proxyUrl = "http://127.0.0.1:7890"     # egress proxy for this backend (any backend may set one)

[[offerings]]
alias = "peng/qwen2.5.7b"                # namespace must be your producer name
backend = "ollama"
upstreamModel = "qwen2.5:7b"             # the real backend id (full tag from `ollama list`)
maxConcurrencyPerUser = 1                # concurrent requests per consumer on this alias
# maxConcurrentUsers = 3                 # distinct concurrent consumers (hub default 3)
# dailyTokens = 1000000                  # shared tokens per Beijing day (default 1M; 0 = unlimited)
# shareWindows = ["00:00-06:00"]         # share-time scheduling — see below
# blockWindows = ["14:00-18:00"]         # the deny-list alternative (mutually exclusive)
# shareDays = "everyday"                 # everyday | weekdays | weekend | today | tomorrow | [dates]
# shareTimezone = "Asia/Shanghai"        # default: this machine's display zone (AWESHARE_TIMEZONE)

One offering exposes exactly one upstream model: consumers call the alias and the hub rewrites the request's model to upstreamModel before dispatch — they can never pick another model. To share more models, add more [[offerings]].

One alias can also speak several wire protocols at once: replace backend = "…" with a list — backends = ["a", "b"] — and the block registers one offering per backend. Registrations are keyed by alias + protocol, so the listed backends must use distinct protocols (the hub rejects a duplicate); no conversion ever happens — each registration relays on its own wire. Consumers call the same alias with whichever SDK they prefer, and producer list / consumer list merge the rows into one.

Per-offering usage caps ride along in the same block; the optional two are enforced by the hub (defaults apply when unset, including for agents that predate them):

| Key | Default | Meaning | Enforcement | |---|---|---|---| | maxConcurrencyPerUser | 1 | concurrent requests per consumer on this alias | 429 PRODUCER_MAX_CONCURRENCY — only after queueWaitMs of queueing (see below) | | maxConcurrentUsers | 3 | distinct consumers with a request in flight on this alias | 429 PRODUCER_MAX_USERS | | dailyTokens | 1000000 | tokens (prompt + completion) shared across all consumers on this alias, per Beijing day (UTC+8); 0 = unlimited | 429 QUOTA_EXCEEDED (resets at Beijing midnight) |

maxConcurrencyPerUser caps each consumer's in-flight requests; maxConcurrentUsers caps in-flight people — a consumer firing 5 parallel requests needs maxConcurrencyPerUser ≥ 5 for itself alone, while the total on the alias is bounded by maxConcurrentUsers × maxConcurrencyPerUser. Coding agents need headroom here: opencode keeps a background title/summary request in flight next to the main one, so on offerings meant for such agents maxConcurrencyPerUser = 2 is the practical floor unless consumers route their small model elsewhere (see Consumer tool configuration). Daily caps count recorded usage (see "Honest limits" below). (Renamed in v0.4.3 from maxConcurrency, which capped the alias's total in-flight requests.)

Admission queueing (queueWaitMs) softens this cap's failure mode without changing its meaning: when the consumer's slot on the alias is busy, the request parks at the hub for up to queueWaitMs (env AWESHARE_QUEUE_WAIT_MS, config.toml queueWaitMs, default 10000, 0 = reject at once) and is admitted the moment the slot frees — a short same-key overlap (the background title request, a second window starting up) resolves invisibly instead of 429ing. Only a slot still busy after the window answers 429, and the denial then names how long it waited. The wait budget is shared across the alias's backup queue (primary and backups draw on one deadline, they do not stack), probes never wait (consumer list --ping must answer fast and tell the truth), and hub status shows the setting on the timeouts line. Waiting holds no upstream resources — just the HTTP connection.

Share-time scheduling (optional, same [[offerings]] block; any of the four keys turns it on): shareWindows = ["00:00-06:00"] shares the alias only inside those wall-clock windows, blockWindows = ["14:00-18:00"] is the deny-list alternative — set one or the other, never both. shareDays scopes the calendar: everyday (default), weekdays, weekend, today, tomorrow (both freeze into concrete dates when the config is read — edit the file again tomorrow for another one-day window) or explicit dates like ["2026-09-08", "2026-09-10"]. shareTimezone names the IANA zone the windows live in; unset it defaults to the writing machine's display zone (AWESHARE_TIMEZONE, else Asia/Shanghai), resolved once at parse time and sent with the registration — the hub enforces it however its own clock is set. A window whose start is later than its end ("22:00-06:00") crosses midnight and belongs to its start day, so weekdays-scheduled 22:00-06:00 stays open into Saturday's small hours but not Saturday night. Days-only schedules (no windows) share all day on matching days. Outside the schedule the hub answers 503 SHARE_WINDOW_CLOSED with a Retry-After of the exact time until the next window; consumer list, hub list offerings and producer list show a SCHEDULE column (daily 00:00-06:00, weekdays !14:00-18:00, 2026-09-08 all day, - when unset, (closed) while shut). Schedules are config like the caps — hot-reload edits them, and hub-hosted models set the same keys in config.produce.toml.

An exhausted daily budget can be reopened mid-day by the offering's owner — no restart, no re-register: aweshare producer refresh ns/model on the producer's machine re-anchors today's window at this moment (usage before it stops counting), --add N raises today's cap by N tokens until Beijing midnight, --clear drops both markers. For hub-hosted models the same verbs live under aweshare hub produce refresh name (hub/ prefix optional). Both commands take --all to bare-refresh every one of their own offerings in a single run. The split is deliberate: quota expansion is the owner's call — the hub operator can restrict a producer's model (admin offering revoke) but never spends more of its upstream budget; grants survive re-registers and any catalog edit (they live in their own alias-keyed table, never on the offering rows) and lapse at Beijing midnight on their own. A permanent raise belongs in the config (dailyTokens), which hot-reloads.

Upstream 5xx handling (degradeOn5xx): a 5xx says the upstream is failing, but not for how long — so it reports before it enforces. Two consecutive 5xx mark the backend unstable in every offering table (always on): the alias stays listed and dispatch continues, consumers just get the warning alongside the verbatim upstream errors. Set degradeOn5xx = true (top-level key in the producer's config.toml, hot-reloads; same key in the hub's config.produce.toml for hub-hosted models) to escalate: the third consecutive 5xx degrades for real — dispatch stops with 503 BACKEND_DEGRADED and the 30s recovery probes bring it back on the first success. A single 5xx is noise (nothing happens); any success resets the count; 4xx and network errors never touch the ladder (request-shaped trouble is not the server's health). The probes are real minimal requests, so an opted-in degraded alias costs a little quota while it waits to recover.

Key hygiene: use dedicated, least-privilege, revocable keys with budget alerts; keep secrets.json at 0600 and out of git/screenshots; rotate on suspected leaks. Before sharing, check the upstream's terms: account rules, subscription limits, forwarding and commercial-use constraints.

Account-login backends (login = "codex") authenticate with the producer machine's own codex login instead of a key — the fixed official upstream is https://chatgpt.com/backend-api/codex, responses wire. Other protocols or base URLs are rejected so the account credential cannot be sent elsewhere. How it behaves:

  • The login is read from ${CODEX_HOME|~/.codex}/auth.json, stays in producer memory only, and is re-read whenever the file changes or a request comes back 401 — so a fresh codex login on the producer machine is picked up without a restart. No secrets.json entry exists for it.
  • Several logins side by side (loginHome): give each codex-login backend its own loginHome — the CODEX_HOME-style dir (absolute, leading ~ expanded) that holds auth.json. One producer can then share two accounts at once; each backend reads and re-reads its own file, and producer doctor reports each login separately. Each account's tokens still refresh only where that login lives (e.g. run the second account's CLI session occasionally); an idle expired login degrades only its own aliases.
  • The producer injects the headers the Codex CLI itself sends, forces store: false, and removes max_output_tokens emitted by some Responses SDKs (the chatgpt backend rejects it).
  • Egress proxy (proxyUrl): when the upstream is only reachable through a proxy (e.g. a ladder such as Clash on http://127.0.0.1:7890), set proxyUrl on the backend — any backend, not just account logins (an api.openai.com key behind the same ladder qualifies). It covers that backend's http and https traffic only, hot-reloads with the config, and survives background/systemd starts, where shell environment variables silently vanish. Precedence: proxyUrl beats HTTPS_PROXY/HTTP_PROXY/ALL_PROXY; the env vars alone still apply to codex-login backends only, so a machine-wide proxy setting can never capture a local Ollama. NO_PROXY is always honored; SOCKS is rejected at config load. producer doctor TCP-probes a configured proxy and fails loudly when it is down, and proxy URLs are redacted in producer config show (they can carry credentials) and never logged.
  • Consumers must speak /v1/responses: Codex CLI (default wire_api), opencode (via @ai-sdk/openai), Cline (OpenAI Native provider). Chat-completions tools and Claude Code cannot use these offerings.
  • Tokens are never refreshed by aweshare: when the login expires the offering degrades (2 consecutive 401s → 503 BACKEND_DEGRADED) and recovers on its own once you re-run codex login — the 30s recovery probe re-reads the file.
  • Sharing a subscription login is higher-risk than sharing an API key — see the compliance section above: the credential is account-wide, and the producer bears suspension of the whole account.

Consumer limits (hub-wide defaults + per-consumer overrides)

Every consumer gets the hub-wide defaults (AWESHARE_CONSUMER_RPS / BURST / CONCURRENCY, see Operations). On top of that, the hub admin can set sparse per-consumer overrides — only the keys you set take effect, everything else keeps the defaults:

aweshare hub limits alice --tpm 60000 --max-total-tokens 5000000  # set (merges into stored)
aweshare hub limits alice                                        # view current overrides
aweshare hub limits alice --clear                                # back to hub-wide defaults

# the same knobs over the admin REST API (PUT / DELETE the same path):
curl -X PUT https://hub.example.com/admin/v1/consumers/alice/limits \
  -H "Authorization: Bearer asa_... (the admin token)" -H "content-type: application/json" \
  -d '{"tpm":60000,"maxTotalTokens":5000000}'

| Key | Meaning | Enforcement | |---|---|---| | rps / burst / maxConcurrent | override the hub-wide rate/inflight defaults for this consumer | 429 RATE_LIMITED | | tpm | max tokens (prompt + completion) in any sliding 60s window | 429 RATE_LIMITED (in-memory window, like the RPS bucket) | | maxTotalTokens | lifetime token budget for this consumer | 429 QUOTA_EXCEEDED (sums usage_events) | | probeBudget | complete consumer list --ping runs per day, shared across aliases; 0 = unlimited | 429 PROBE_BUDGET_EXCEEDED (counts runs since Beijing midnight) |

Honest limits: token-based caps count what upstreams report — Ollama streams report no usage, so they contribute 0. Both TPM and the lifetime budget are observed-usage thresholds, not hard reservations: one request can cross the threshold, and concurrent requests that start before earlier usage is recorded can overshoot it further. Once recorded usage has reached the threshold, new requests are rejected.

Alias backups (hub-side failover)

An alias dies in ways no retry fixes: the producer drops offline, the alias burns its daily budget, its backend degrades, the operator blocks it. Without help, every consumer pointed at that alias breaks until someone hand-edits a model name. Alias backups fix that on the hub side: the operator gives each alias an ordered list of backup aliases, and when the alias cannot serve, the hub retries the queue in order before answering with the alias's own error. The consumer configures one model and keeps it — a backup that serves stamps the response with x-aweshare-fallback: <alias actually served>, so the swap is visible on the wire, in the usage log (each attempt is its own row, under the alias that served it) and in the model-honesty audit (keyed to the alias that answered).

Two ways to manage the queues — a file and a command over the same data:

# ~/.aweshare-hub/config.backups.toml — key = alias, value = ordered backups
"peng/qwen3-coder" = ["alice/qwen3-coder", "hub/glm-4.6"]
aweshare hub backups                                # list every queue
aweshare hub backups peng/qwen3-coder               # show one
aweshare hub backups peng/qwen3-coder --add alice/qwen3-coder,hub/glm-4.6
aweshare hub backups peng/qwen3-coder --add hub/glm-4.6 --first   # prepend
aweshare hub backups peng/qwen3-coder --remove hub/glm-4.6
aweshare hub backups peng/qwen3-coder --clear

Semantics, deliberately close to a queue of literal strings:

  • The queue is exactly what was written — no implicit hops, a backup's own queue is never expanded, each entry is tried at most once per request. Entries must be distinct, may not include the alias itself, and may name aliases that don't exist yet (producers come and go); an entry with no offering on the request's wire protocol is simply skipped.
  • Failover triggers on alias-side faults only: producer offline, degraded backend, blocked alias, closed share window, spent daily budget, per-alias concurrency caps — and pre-head relay failures (the producer accepted the dispatch but never delivered a response head: tunnel closed, backend timeout). Once the response head reached the consumer the stream is committed — no mid-stream swaps. Consumer-side denials (rate limits, probe budget, the consumer's own token budget) never fail over: swapping aliases cannot rescue the consumer's own limits, and consumer list --ping must tell the truth about the alias it names, so probes never fail over either.
  • Each attempt runs full admission again — the backup's per-alias caps and daily budget apply as they would for a direct request, and the backup's usage lands on its own account, not the failed alias's.
  • When every entry fails, the response is the primary's original error — the one the consumer asked about is the one worth explaining.

The file lives in the hub data dir next to config.toml (~/.aweshare-hub/config.backups.toml; Docker: /data/config.backups.toml) and hot-reloads like every other hub config — edits apply within ~2s, a broken file is logged while the previous queues keep serving, an invalid file fails hub serve at startup. hub init scaffolds it (commented, empty). Like config.toml itself it is a this-machine file, not part of the admin REST API: on a remote hub, run the command there (ssh / docker exec) or edit the file.

Endpoints and errors

| Endpoint | Purpose | |---|---| | POST /v1/chat/completions · POST /v1/messages · POST /v1/responses | inference (Bearer or x-api-key) | | GET /v1/models | every alias registered on the hub, with status | | GET /v1/catalog | every offering on the hub — producer, alias, protocol, status, the per-offering caps, live in-flight occupancy (activeUsers/activeRequests) and today's used/remaining tokens (discovery view for aweshare consumer list) | | GET /healthz | liveness | | GET /admin/v1/offerings | registered offerings with live status, caps, in-flight occupancy and today's used tokens — admin sees everything, a producer token only its own slice (aweshare producer list) | | /admin/v1/* | token/limit/usage management (admin or producer token) · usage: GET /admin/v1/usage (newest-first log, same since window) and GET /admin/v1/usage/summary (group=consumer-alias\|consumer\|alias, since=30m\|12h\|7d\|all, default all, consumer/producer/alias filters; every role sees its own slice) · consumer limit overrides: GET/PUT/DELETE /admin/v1/consumers/{name}/limits (admin only) |

Error semantics: 401 invalid key · 401 TOKEN_REVOKED suspended token (ask the operator to restore it) · 403 HUB_FULL producer capacity reached · 404 unknown alias · 400 PROTOCOL_MISMATCH protocol/alias mismatch · 429 rate limit, TPM or producer concurrency cap (PRODUCER_MAX_USERS = distinct-consumer cap; QUOTA_EXCEEDED = lifetime or daily token budget hit) · 502 upstream/tunnel failure (upstream 4xx/5xx passes through verbatim) · 503 producer offline / backend degraded · 504 timeout. Errors carry {error:{code,message,requestId}}; the requestId spans both sides' logs.

Usage metering: one row per request (alias, declared upstream model, response-reported model when available, status, duration, byte counts, best-effort token counts), zero content stored. aweshare hub list usage (and aweshare producer list usage on a producer's machine, scoped to its own models) answers "who used how much" by default: server-side aggregation on the hub's SQLite, one row per consumer × model, most recently used first — with request/error counts, best-effort token totals, an explicit unknown-token count (streaming backends that report no counts) and mean duration; the table closes with a TOTAL row summing every group (duration averaged request-weighted). The window defaults to all time and is printed with the table (--since 30m\|12h\|7d\|…\|all); --group-by consumer rolls up to per-person totals, --group-by alias to per-model totals; --sort re-orders the rows (consumer, producer or model, alphabetical with newest first within; tokens or requests, busiest first — tokens keeps a person's rows together, the pre-0.6.1 default order). --details switches to the per-request log (GET /admin/v1/usage; admin sees everything, producers and consumers their own slice, rows carry the consumer/producer names).

Command reference

Both sides at a glance — details in the sections above.

Hub (operator) — needs the admin token from aweshare hub init; for a hub on another server, run these on the server or set AWESHARE_HUB_URL:

| Command | Purpose | |---|---| | aweshare hub init | create data dir + admin token (printed once) | | aweshare hub serve [--host H] [--port N] | run the hub — the only runner; a config.produce.toml in the data dir mounts automatically: its [[backends]]/[[offerings]] sections become hub/… offerings served in-process (keys in the data dir's secrets.json; edits hot-reload) | | aweshare hub produce init | scaffold config.produce.toml + empty secrets.json in the data dir (kept if they exist); also initializes the data dir, db, pepper and admin token | | aweshare hub admin invite mint [--role producer\|consumer] [--name NAME] [--count N] [--expires-in D\|none] | mint one-time invite codes (asi_…, printed once; re-view with list invites --reveal); --expires-in (default 7d, none = no deadline) bounds only how long the code stays redeemable — the token a redeem mints never expires, revoke/restore is its lifecycle handle; producer codes: bound (--name) or unbound (name + email at redeem, --count batches); consumer codes: always bound to one name | | aweshare hub admin invite revoke N… · aweshare hub admin invite restore N… | kill invite(s) / undo — a redeemed code suspends the producer it minted, restore revives both; several ids per call (spaces or commas), one failure does not stop the rest | | aweshare hub admin invite extend N… --expires-in D\|none | reset a pending invite's redemption window from now (none = no deadline); a redeemed invite answers redeemed: true — its token never expires, there is nothing to extend | | aweshare hub admin offering revoke ALIAS… · aweshare hub admin offering restore ALIAS… | the per-alias scalpel between doing nothing and revoking a whole producer: revoke offerings (every protocol row of each alias) — new requests get 503 OFFERING_BLOCKED, list offerings shows blocked, the producer's other offerings keep serving; several aliases per call. Manual revokes survive re-registers; auto revokes (model mismatch, see autoBlockModelMismatch) clear once the producer re-declares a different upstreamModel | | aweshare hub list [invites\|producers\|consumers\|offerings\|usage] | read hub state, one table per noun (default: invites) | | aweshare hub list invites [--reveal] [--token] [--json] | the invite ledger: every code, the identity it minted and its lifecycle (pending/used/suspended/revoked/expired — expired marks a pending code whose redemption window closed; redeemed identities never expire); --reveal re-shows the codes, --token the minted tokens with last seen | | aweshare hub list producers [--json] · aweshare hub list consumers [--json] | the rosters: name, status (active/suspended/built-in), online state (producers), last seen, created | | aweshare hub list offerings [--json] | the catalog: offerings counted per deduplicated alias (several protocols → one verdict, the worst), one row per alias — the same columns as consumer list and producer list, worst status first — with observed model, caps, live occupancy (IN USE n/max) and today's remaining daily tokens | | aweshare hub status | the live dashboard: capacity (producer slots, consumers, offering counts), a last-5m requests/ok-rate/errors line from the usage summary (hub-admission 429s are not metered), admission-rejection pressure (top throttled alias/consumer) and the effective consumer defaults | | aweshare hub limits NAME… [--rps N] [--burst N] [--max-concurrent N] [--tpm N] [--max-total-tokens N] [--probe-budget N] [--clear] [--json] | show, merge or clear consumers' limit overrides, one consumer or several per call (unset keys keep the hub-wide defaults) | | aweshare hub backups [ALIAS] [--add A[,A…]] [--first] [--remove A[,A…]] [--clear] [--json] | per-alias failover queues in the data dir's config.backups.toml (hot-reloads; this machine's file, like config.toml itself): bare ALIAS shows one queue, no ALIAS lists all; --add appends (comma-separate several), --first prepends, --remove deletes entries, --clear drops the queue — see Alias backups | | aweshare hub list usage [--details] [--consumer NAME] [--producer NAME] [--alias ns/model] [--group-by consumer-alias\|consumer\|alias] [--since 7d\|all] [--sort time\|consumer\|producer\|model\|tokens\|requests] [--limit N] [--json] | who used how much (default): aggregate per consumer × model, most recently used first — requests, errors, rate, best-effort token totals, unknown-token count, mean duration, closing TOTAL row; window defaults to all and is printed with the table; --sort re-orders (consumer/producer/model alphabetical, tokens/requests busiest first) · --details: per-request log, newest first, windowed by the same --since, zero content stored, each row naming its consumer | | aweshare hub produce refresh NAME… [--add N] [--clear] [--json] · aweshare hub produce refresh --all [--json] | reopen hub-hosted models' daily token budgets mid-day (hub/ prefix optional; several names per call): bare call re-anchors today's window at this moment, --add N raises today's cap by N tokens until Beijing midnight, --clear drops both markers. Hub-hosted (hub/…) offerings only — a producer's models are its own to refresh. --all bare-refreshes every hub/… offering with a daily cap in one run (unlimited ones are reported and skipped; one failure does not stop the rest) |

Token issuance runs through invites (both roles). admin, limits and list usage are thin wrappers over the admin REST API (/admin/v1/*, see Endpoints and errors) — curl works too.

Every list table and status print aligned columns by default; append --json (where documented) for the raw API rows.

Producer — runs on the producer's machine (the one with the backends):

| Command | Purpose | |---|---| | aweshare producer init [--hub URL] [--token asp_…] | write config templates into ~/.aweshare | | aweshare producer join --hub URL --code asi_… [--name NAME --email [email protected]] | redeem an invite code into a producer token and write it into the config (--name/--email for unbound codes; probes the hub first — plain HTTP outside the LAN needs --allow-http) | | aweshare producer config path · config show · config edit | locate / inspect (secrets redacted) / edit the config | | aweshare producer doctor [--status] | diagnose end to end: background instance, config, backend probes, hub (including how many of your offerings are registered), recent log (--status skips the network probes for an instant answer) | | aweshare producer list [offerings] [--json] [--all] | what this producer has registered on the hub — alias, protocol, live status, caps, live occupancy (IN USE, distinct consumers in flight right now), today's token use — plus the local background instance state and drift against config.toml (hubUrl/token come from config.toml); --all: every producer's registrations, the discovery view | | aweshare producer list usage [--details] [--consumer NAME] [--alias ns/model] [--group-by consumer-alias\|consumer\|alias] [--since 7d\|all] [--sort time\|consumer\|model\|tokens\|requests] [--limit N] [--json] | who used this producer's models (the producer token scopes the hub's metering to its own slice): aggregate per consumer × model by default, most recently used first (--sort re-orders), closing TOTAL row, window defaults to all · --details: per-request log, newest first, same --since window, each row naming its consumer | | aweshare producer status | the live one-glance summary: local process, config counts, registered-offering health rollup and drift — the full table is list offerings | | aweshare producer start [--background \| --install] | connect and relay (long-running; --background detaches it — logs to ~/.aweshare/producer.log, pid to producer.pid; --install installs and starts a launchd/systemd service for boot auto-start and crash recovery) | | aweshare producer reload | signal the background producer (SIGHUP) to re-read config.toml + secrets.json and re-register its offerings on the open tunnel — no disconnect; a broken config keeps the previous values | | aweshare producer refresh ALIAS [--add N] [--clear] [--json] · aweshare producer refresh --all [--json] | reopen one of this producer's offerings mid-day, hub-side and effective at once (works even while the agent is stopped): bare call re-anchors today's window at this moment — usage before it stops counting; --add N raises today's cap by N tokens until Beijing midnight (replaces an earlier bonus); --clear drops both markers. Own offerings only; a permanent raise belongs in dailyTokens. --all bare-refreshes every registered offering with a daily cap in one run (unlimited ones are reported and skipped; one failure does not stop the rest) | | aweshare producer stop [--purge] | stop the current producer run (SIGTERM, SIGKILL after 10s) and clean up its pidfile; --purge also removes an installed system service so it no longer auto-starts |

Consumer — two commands, on the consumer's machine; day-to-day they point a standard SDK at the hub (see Consumer tool configuration):

| Command | Purpose | |---|---| | aweshare consumer join --hub URL --code asi_… [--allow-http] | redeem a consumer invite into an asc_ token — printed once with ready-to-paste SDK env vars (save it; the operator can re-view it with hub list invites --token) | | aweshare consumer list --hub URL --token asc_… [--all] [--json] | discovery view of the hub: online offerings by default (degraded/unstable stay listed; --all includes offline) — every producer, alias, protocol, status, the per-offering caps, live occupancy (IN USE n/max — distinct consumers with a request in flight right now; an alias at max/max admits no new consumer until one settles) and remaining daily tokens | | aweshare consumer list --hub URL --token asc_… [--all] [--alias a,b] --watch · --refresh N | live monitor: the plain discovery view redrawn in place on an interval — --watch every 60s, --refresh N[s\|m\|h] at a custom one (floor 5s, gentle on the hub; implies --watch), the header line stamping each refresh. Terminal only (non-TTY stdout fails with a hint); Ctrl+C exits cleanly with code 0. The free view only, by design: --ping consumes producer quota and probe budget, so it stays a deliberate one-shot and combining them fails loudly; --json is rejected too — scripts should poll on their own schedule. A failed refresh (hub or network blip) becomes the frame's content and the watch keeps going | | aweshare consumer list --hub URL --token asc_… [--all] [--json] [--ping] [--ping-table] [--alias a,b] | discovery view of the hub: every producer, alias, protocol, status, the per-offering caps, live occupancy, remaining daily tokens and LAST SEEN — the hub's freshness evidence (how long ago real traffic or a recovery probe last proved an offering served; - = never). --ping adds the consumer's own proof: one minimal real model request per offering row (SDK-shaped, max_tokens:1) through the same /v1 endpoints, reporting RESULT, round-trip TIME and the served model — a FAIL passes the hub/upstream error through verbatim. Rows stream in one line per ping as they land; --ping-table (implying --ping) shows progress on stderr and prints a FAIL table (with HTTP status) and an OK table when the run completes instead. Real calls consume the producer's quota, so scope with --alias; the hub budgets complete --ping runs (10/day per consumer by default, consumerProbeBudget), not individual rows; exit 1 on any pinged failure (plain list always exits 0) |

CLI maintenance: aweshare self-update [--check] updates the npm-installed CLI (--check only compares versions). Two post-upgrade steps: a producer installed as a system service needs aweshare producer stop --purge + aweshare producer start --install before the running service picks up the new version (why: see Updating), and the aweshare skill should be refreshed with aweskill update aweshare (direct-copy installs: re-copy per README.ai.md).

Operations

No always-on box to share from, or looking for models others share? The project's developer runs a community hub at https://aweshare.wehuman.top (invite-based — request a code at [email protected]); docs/community-hub/ is a step-by-step guide for connecting as a producer or consumer (中文版).

The hub reads config.toml from its data dir (~/.aweshare-hub/config.toml; Docker: /data/config.toml). aweshare hub init writes the template with every key commented out — uncomment to override a default. Keys use the same names as below, camelCase (consumerRps, headTimeoutMs, …). Precedence: serve flags (--host/--port) > env vars > config.toml > defaults. A broken file (invalid TOML, unknown key, non-positive value) fails fast at startup with the key named; AWESHARE_HUB_DATA_DIR itself stays env-only (it locates the file).

Hot reload: every tunable in the table except host/port applies live on SIGHUP (kill -HUP <pid>; Docker: docker kill -s HUP aweshare-hub) — the new file is validated first, and a broken edit is logged while the previous values keep serving. Env vars are fixed at process start, so keys pinned by AWESHARE_* ignore the reloaded file (same precedence as startup); host/port need a restart. Producer-side offerings and caps reload via aweshare producer reload. config.backups.toml (per-alias failover queues, see Alias backups) rides the same watcher: edits hot-reload within ~2s, a broken edit keeps the previous queues, an invalid file fails hub serve at startup.

Hub-hosted models (hub produce): config.produce.toml carries [[backends]] and [[offerings]] sections (producer format; alias namespace hub/… — bare names are auto-prefixed) with upstream keys in secrets.json next to it (chmod 600). config.toml remains exclusively for Hub runtime settings. Those offerings appear in the catalog under producer hub and are served by the hub process directly — no tunnel, and they never count against AWESHARE_MAX_PRODUCERS. Caps (maxConcurrencyPerUser, maxConcurrentUsers, dailyTokens), usage metering and consumer limits apply exactly as for remote producers. The built-in hub producer is not an identity (no token, no invite, cannot be revoked); it appears in the hub list producers roster — status built-in — only while it carries offerings. An enabled = true|false key at the top of the file is the catalog's master switch: false unloads every hub/… model within the hot-reload window (the definitions stay; flip back to bring them all back) — the produce-side counterpart of revoking a remote producer's invite. A non-boolean value fails loudly instead of silently serving or unloading. Catalog and key edits hot-reload like the tunables; a broken catalog keeps the previous one and is logged.

| Env var | Default | Purpose | |---|---|---| | AWESHARE_HUB_DATA_DIR | ~/.aweshare-hub | data dir (SQLite/pepper/admin token/config.toml; volume-mount = backup) | | AWESHARE_HUB_PORT / HOST | 8787 / 0.0.0.0 | listen address | | AWESHARE_CONSUMER_RPS / BURST / CONCURRENCY | 10 / 20 / 8 | per-consumer limits | | AWESHARE_CONSUMER_PROBE_BUDGET | 10 | complete consumer list --ping runs per consumer per day; 0 = unlimited | | AWESHARE_HEAD_TIMEOUT_MS / IDLE_TIMEOUT_MS | 120000 / 120000 | response-head timeout / stream idle timeout | | AWESHARE_QUEUE_WAIT_MS | 10000 | how long a request may wait for a per-user admission slot before 429 — short same-key overlaps (a coding agent's background title request, a second window) queue through instead of failing at once; 0 = reject immediately | | AWESHARE_MAX_BODY_BYTES | 32MB | request body cap | | AWESHARE_INVITE_REDEEM_PER_MIN | 10 | redeem entry global insurance budget (valid-format attempts shared by every origin) | | AWESHARE_INVITE_REDEEM_PER_IP_MIN | 5 | redeem entry per-origin-IP bucket (CF-Connecting-IP behind a tunnel/proxy) — one visitor cannot monopolize admission; both keys reload via SIGHUP | | AWESHARE_MAX_PRODUCERS | 10 | max active producers — token issuance (admin API), invite redeem and restore refuse with 403 HUB_FULL when full | | AWESHARE_AUTO_BLOCK_MODEL_MISMATCH | false | auto-block an offering after 2 consecutive successful responses report a different model than declared. Off by default: mismatches are logged once (flip) and visible in hub list offerings / consumer list / list usage --details either way — the hub is report-only until the operator opts in. An auto block clears when the producer re-declares a different upstreamModel; an explicit manual admin offering revoke takes precedence and always persists; reloads via SIGHUP | | AWESHARE_OFFLINE_AFTER_MS | 432000000 (120h) | continuous-degraded duration before an offering renders offline — the honest verdict for a backend that has not recovered in days. Display and catalog only: dispatch is already cut at degraded, recovery probes keep running, and a late success returns the offering straight to online. Per offering (its backend), never per producer: the producer's other models keep their own verdicts. The 503 gains how long it has been dead, hub status lists the dead aliases, and /v1/catalog carries degradedSince. Upgraded agents report the exact streak start; older ones get a hub-side first-seen stamp (reset by a hub restart or producer reconnect). 0 = stay degraded forever; reloads via SIGHUP | | AWESHARE_HUB_CONTACT_EMAIL | unset | contact address shown on the browser landing page (GET / with Accept: text/html) where visitors request an invite — a static bilingual EN/中文 page (toggle via ?lang=, first visit follows Accept-Language); unset shows generic "contact the hub operator" wording; reloads via SIGHUP | | AWESHARE_NO_UPDATE_CHECK | unset | set to 1 to disable the passive update reminder | | AWESHARE_TIMEZONE | Asia/Shanghai | display zone for every human-readable time the CLIs print (table cells, since … windows, log lines). Any IANA name; the wire, SQLite and --json stay UTC ISO. Read by whichever CLI renders, so it also applies to docker exec — set it on the container to change hub list/hub status output. Not a server tunable: no SIGHUP reload, no config.toml key |

Health: agent heartbeats every 15s, silent 45s = dead; backends with 2 consecutive AUTH/QUOTA failures auto-degrade (alias shows degraded, dispatch stops), 30s probes recover; 2 consecutive upstream 5xx show unstable (dispatch continues), a 3rd degrades only with degradeOn5xx opted in. A degraded streak older than AWESHARE_OFFLINE_AFTER_MS (default 120h) escalates that offering to offline — per offering, the producer's other models unaffected; probes keep running, and a recovery returns it straight to online (the escalation is never a block, and never stops the daily last-gasp probe). A new connection with the same producer token replaces the old one (latest-wins).

Model honesty: an offering's upstreamModel is the producer's claim — the hub compares it with the bounded model id reported by each successful response (model / message.model / response.model). This is consistency evidence, not proof of the model's underlying weights: a producer or upstream router can still rewrite that metadata. The observation rides the existing usage meter, never rewrites the relayed response, and is scoped to the same alias, protocol and current declaration. It appears in the OBSERVED MODEL column, list usage --details, and /v1/catalog as observedModel, observedAt, modelMatch and the backward-compatible modelVerified. The catalog also carries hubCheckAt — the newer of that observation and the serving side's heartbeat-reported last success — rendered as the LAST SEEN column: how long ago the hub's evidence says the offering actually served (- = never). Comparison is token-aware: exact ids and explicit date/revision suffixes are affirmative; vendor prefixes are tolerated; adjacent variants such as gpt-4/gpt-4o and gpt-4o/gpt-4o-mini mismatch; a less-specific response is marked ?/insufficient, not verified. Handling is tiered: default report-only, manual hub admin offering revoke ALIAS or whole-producer hub admin invite revoke, and opt-in auto-block after 2 consecutive mismatches. Manual blocks override automatic ones. producer doctor probes every distinct configured model and reports the response-named id with the same evidence boundary.

Updating a npm install: aweshare self-update (asks before installing; --check only shows versions). The CLI also reminds you at most once a day when a newer npm release exists, and that reminder nudges a skill refresh too. A producer installed via --install does not hot-swap to the new version: the service pins absolute node/cli.js paths while npm replaces the files in place, so the running process loads the new code only on a restart — aweshare producer stop --purge + aweshare producer start --install switches immediately, and a reboot or crash-restart works too. Refresh the aweshare skill with aweskill update aweshare (direct-copy installs: re-copy per README.ai.md) so agent-side docs track the CLI.

Updating a Docker deployment: docker compose pull && docker compose up -d. State lives in the ./data volume; producers redial automatically after the restart, and consumers see 503 only during the brief restart window.

Known limitations (v1)

  • No cross-protocol conversion: an alias speaks exactly one wire (openai chat, anthropic messages, or openai responses).
  • Account-login backends never refresh tokens: an expired codex login degrades the offering until someone runs codex login again on the producer machine (recovery is automatic once the file changes).
  • Ollama streams carry no usage → token counts recorded as NULL (best effort by design).
  • Single hub instance + SQLite; no horizontal scaling.
  • Corporate proxies may block the WebSocket tunnel (environmental limit).

Development

pnpm install
pnpm test        # 197 tests: protocol / hub contract (fake agent vs real hub) / agent unit / e2e (real SDKs)
pnpm build       # tsc -b, whole monorepo
pnpm check       # biome

To run the CLIs from a source checkout without node apps/.../dist/cli.js, link them once after building:

npm link                 # or: pnpm link --global — exposes the single `aweshare` bin
aweshare hub serve
aweshare producer doctor

Releasing: push a v* tag with a matching ## [x.y.z] section in docs/CHANGELOG.md; CI publishes the aweshare package to npm via Trusted Publishing (OIDC, no token secret), pushes the Docker image to ghcr.io/wehuman01/aweshare, and mirrors user-facing docs to the public repo (wehuman01/aweshare).

Layout: packages/protocol (shared wire protocol) · packages/producer-core (shared producer runtime) · apps/hub (HTTP+WS+SQLite+CLI) · apps/agent (CLI). Design docs live in docs/specs/; the changelog in docs/CHANGELOG.md; contribution scope in CONTRIBUTING.md.

Support

If aweshare saves you a subscription or a GPU box, consider supporting it:

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

aweshare is free to use and self-host. Sponsors keep it maintained — thank you.

Licensed under the aweshare Proprietary License — free to use and self-host, no redistribution. See LICENSE.

Awesome Ecosystem

aweshare 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.