Skip to main content

hugpy-core

Central for a Hugpy fleet. It is one aiohttp service that is three things at once:

  • the router: the OpenAI-compatible /v1 front door. It picks a worker per call and proxies the call to that worker's hugpy-wrapper.
  • the fleet API under /api/fleet/*: worker registration, mirrored history, derived model and allocation tables, central drives, Hub downloads, grading and training.
  • the console: the @hugpy/ui shell at /, /console, /docs and related paths. Core also proxies the installed arms (media, video, fleet).

Distribution hugpy-core 0.4.41, import hugpy_core. The live service is basic mode (hugpy_core.basic). The pre-0.2 Flask application is still in the tree but is not live (see Legacy app).

Core is under active development. This README describes the tree as of 2026-10-15. For the version-by-version record, see BASIC-CORE.md. The package index is docs/packages.md, and the system view is docs/architecture.md.

What runs

What Where
Service hugpy-core.service: /opt/hugpy-core/venv/bin/python -m hugpy_core.basic.cli serve, user hugpy-core, EnvironmentFile=/etc/hugpy/core.env
Bind HUGPY_CORE_HOST / HUGPY_CORE_PORT. On ae: 192.168.1.100,10.99.0.3,127.0.0.1 port 7020. The first address is core's own URL, and the WireGuard address is how a-brain reaches it.
Endpoint file /etc/hugpy/core.endpoint.json ({service, profile: basic, version, host, port, url, token_file, token_env: HUGPY_ROUTER_KEY, store})
Database hugpy-core-postgres.service: core's own Postgres 16 cluster at /var/lib/hugpy-core/pg. See Postgres cluster.
State /var/lib/hugpy-core (service account only); training environment under /var/lib/hugpy-core/training
Public origin https://dev.hugpy.ai. nginx proxies / to 192.168.1.100:7020 with a LAN-only allow list; /apt/ and /dev-fleet/ are served separately.
Venv contents hugpy-core 0.4.40, hugpy-wrapper 0.1.120 (library only), abstract_toolserver 0.0.98 (library and db CLI), hugpy-tools 0.2.7, aiohttp, psycopg

The wrapper inside core's venv is a library. Core uses its SQLite store machinery, its KeyAuth API-key scheme, its marker derivation and its transfer helpers. Installing core never installs, starts or reconfigures a wrapper service. A wrapper on the same box is a separate deb with its own hugpy-wrapper.service.

hugpy-legacy-adapter.service (/opt/hugpy-legacy-mock/adapter.py, the old UI on 7025, fed from core's API) is a separate host-side shim. It is not part of this package.

The hugpy-core CLI

The hugpy-core script runs hugpy_core.install.cli:main. Every argument list goes to the basic CLI (hugpy_core/basic/cli.py) unless it starts with legacy. A bare hugpy-core, or one that starts with a --flag, means serve.

Subcommand What it does
serve [--host H[,H2…]] [--port P] [--home DIR] [--headless] Runs basic core. Defaults come from HUGPY_CORE_HOST (127.0.0.1), HUGPY_CORE_PORT (7020), HUGPY_CORE_HOME and HUGPY_CORE_HEADLESS. --headless is the router alone: /v1, the fleet API and the event stream, with no console.
install --system [--host] [--port] [--without-local-wrapper] [--auth-open | --auth-base URL] [--central-drive PATH [--central-drive-max GIB]]… [--no-central-drives] [--hf-token T | --no-hf-token] [--headless | --with-console] An idempotent system install, which the deb's postinst runs. It creates the service account, provisions the Postgres cluster through abstract-toolserver db install --system --account hugpy-core, writes /etc/hugpy/core.env and the endpoint file, and installs both units. It also registers a local wrapper it finds in /etc/hugpy/wrapper.endpoint.json, without restarting that wrapper.
status Prints the endpoint file and whether /health answers.
keys [--url] create --name N [--scope use|admin] [--expires-days D] | list [--all] | revoke ID Manages machine-client API keys through the running core.
workers add --ssh USER@HOST [--host] [--name] [--advertise] [--apt URL] [--central-url] [--central-key] [--ssh-opt O]… [--vram-max/--ram-max/--cpu-max/--storage-max] [--dry-run] Creates a worker on another host over key-based ssh with passwordless sudo. It adds the apt source (default https://dev.hugpy.ai/apt) and installs hugpy-wrapper. It runs a legacy-worker step (stops hugpy-worker*, hugpy-fleet-worker*, fitevict-frontdoor* and fitevict-serve* units, and hands their model stores to the new wrapper) and a ufw step. It then runs fitevict install --system --central … --central-key … and confirms that the worker is listed here.
workers list [--central-url] [--central-key] Lists the workers this central knows.
register --url U [--key K] [--name N] [--direct] Registers a wrapper as a worker through the running core, or writes the database as the service account with --direct. The wrapper's own install calls this when core is on the same box.
download HUB_ID [--filename F | --include P…] [--revision R] [--drive PATH] [--wait] Queues a Hugging Face repo onto a central drive.
downloads Shows the download queue.
hub-token [--set T | --clear] Sets or clears the Hugging Face credential the downloader presents.
grade [MODEL…] [--worker W]… [--limit N] [--wait] Starts a grading run: background rubric calls whose verdicts are read back from the call log.
grades [--model M] Shows the derived grades.
legacy … The pre-0.2 runtime. legacy install|uninstall|status runs the old orbit installer; anything else starts the Flask app. Neither works in the live venv, which lacks the [legacy] extra.

The /v1 relay (the router)

hugpy_core.router is the former hugpy-router package, folded in at 0.4.0. hugpy_core/router/serve/app.py registers these routes, and basic/app.py:create_app builds on that app:

Route Behaviour
POST each path in PROXY_ROUTES Proxied to the chosen worker, streaming both ways.
GET /v1/models The fleet catalog: the union of the workers' catalogs plus workers, boxes, resident_on, resident and available.
GET /health, /v1/health Liveness.
GET /v1/capabilities {service, version, profile: basic, scope: fleet, contract_version: 2, headless, router: folded, …}
GET /v1/telemetry/resources[?worker=] Each worker's resource snapshot, with freshness.
GET /v1/router/state, /v1/queue, /v1/telemetry Admin views: box links, central's queue, the dispatch log (route_log).
GET|POST /v1/keys, POST /v1/keys/{id}/revoke Admin: API keys.
GET /v1/boxes, * /v1/boxes/{box}/{tail} Core's view of every box, and a relay to each wrapper's own API.
GET /v1/transfer/{model_key}/{manifest|file|chunksums} Core serves its verified central-drive copies the way a worker does.

PROXY_ROUTES is defined in hugpy_core/router/imports/constants.py:

/v1/chat/completions   /v1/completions        /v1/embeddings
/v1/audio/transcriptions  /v1/audio/translations  /v1/audio/speech
/v1/images/generations /v1/images/edits       /v1/images/variations
/v1/videos/generations /v1/summarize          /v1/keywords

How a call is placed (core 0.4.1 and later):

  • The pick follows the derived allocation. After every derive, core publishes per (worker, model) whether the model is resident, busy, loading or available, together with the wrapper's own fit map per quant and option. The router takes a serving box with the matching contract first. Otherwise it takes a box whose fit map says the call's options fit. A box with no verdict yet, or none that fits, is refused with that reason. See GET /api/fleet/allocation.
  • A call goes to one box only. A refusal before the first byte is recorded in allocation_refusals and returned as 503 box_refused. It is never re-sent elsewhere; the bounce-back of hugpy-router 0.0.4 and earlier is gone.
  • Central queue. A call whose allocated boxes are all busy or loading waits for a ticket in central's queue. Ordering is by predicted time-to-output, using the wrapper's own load estimates. The queue is configured by HUGPY_QUEUE_POLICY (eta), HUGPY_QUEUE_BYPASS_MAX (3) and HUGPY_QUEUE_MAX_WAIT_S (120).
  • Keys. Client keys are hpk_…. The router's admin key is HUGPY_ROUTER_KEY in core.env. Who needs a key is set by HUGPY_ROUTER_REQUIRE_KEY (outside by default) together with the trusted networks. /v1/chat/completions and /v1/models also accept a signed-in admin console session.

Answers carry X-Hugpy-Box and the router request id. A queued call also carries X-Hugpy-Queue-Ticket and X-Hugpy-Queue-Wait.

Arms: discovery and proxy (0.4.33 and later)

An arm is a separate deb that runs its own loopback service: hugpy-media on 7340, hugpy-video on 7341, and hugpy-agent on 7342. Core never installs an arm into its own venv. Each arm's postinst writes /usr/share/hugpy/arms.d/<arm>.json (HUGPY_ARMS_DIR overrides the path):

{"arm":"media","version":"0.3.3","upstream":"http://127.0.0.1:7340",
 "api_prefix":"/api","categories":["media"],"ui":"/media/","manifest":"/api/media/manifest"}

hugpy_core/basic/arms.py reads that directory on every request. There is no probe loop and no heartbeat, and only each arm's manifest is cached (GET /api/arms?refresh=1 clears it).

  • /api/<category>/… is proxied to the arm that claims the category. The catch-all is registered after every core route, so core routes always win. An unclaimed path answers exactly as it did before.
  • /api/arms/<arm>/… is an address-by-name alias.
  • /media/, /video/ and /fleet/ go to the arm's own SPA. With no arm installed, they fall back to the console.
  • Bodies stream unbuffered in both directions: uploads, Range/206 responses and SSE. Core never forwards the console's Cookie or Authorization headers to an arm; it adds X-Forwarded-*. If an arm is down, the request gets a 502 that names it.
  • /api/arms lists the toolsets. /api/fleet/addons marks an arm installed when its descriptor is present. /api/console/bootstrap marks the Media / Video / Fleet nav entries present.

On ae today, arms.d/ holds agent.json (0.1.118, /fleet/), media.json (0.3.3) and video.json (0.3.4, categories video and movie). For the arm-side contract, see docs/arms.md.

The fleet API

The full route table is in hugpy_core/basic/app.py:create_app, and each area's behaviour is in BASIC-CORE.md. The main areas:

Area Routes
Workers GET|POST /api/fleet/workers (register, rename, block), …/{name}/models, …/{name}/actions (refresh-models, evict, copy, reap), …/{name}/settings (pair knobs), …/{name}/box-settings, …/{name}/capacity, …/{name}/drives; worker keys GET|POST /api/fleet/worker-keys; the ssh join POST /api/fleet/workers/join, GET …/join-key, GET …/join/{job}
Models and derived tables /api/fleet/models[/{key}], /api/fleet/knobs, /api/fleet/allocation, /api/fleet/refusals, /api/fleet/feasibility, /api/fleet/copies, /api/fleet/serve-times, /api/fleet/metrics, /api/fleet/evictions, /api/fleet/calls, /api/fleet/history, /api/fleet/drives
Central drives /api/fleet/central/drives[/scan], /api/fleet/copies/plan, /api/fleet/transfers, DELETE /api/fleet/central/copies/{model_key}
Hub downloads /api/fleet/downloads[/{id}[/{verb}]], /api/fleet/hub/{spec,search,token}; the legacy console paths /api/jobs, /api/llm/jobs, /api/llm/repos/download, /api/hf/spec, /api/search
Grading /api/fleet/grades, POST /api/fleet/models/{key}/grade, /api/fleet/benchmarks/{run,runs,status,cancel,judge}; the legacy aliases /api/llm/benchmark/*
Training /api/fleet/training, …/env, …/datasets[/{name}], …/runs[/{id}[/cancel]]
Help /api/fleet/help/model, POST /api/keeper/help/{ask,report}, /api/fleet/harness/config
Console /api/console/bootstrap, /api/arms, /api/readiness, /api/auth/config, /api/auth-svc/*, /api/keys*, /api/llm/events (SSE), /api/llm/snapshot, /api/llm/queue, /api/llm/env-profiles, /api/llm/db/{sources,tables,rows}, /api/models/database/{model}/workers/{worker}/knobs; the per-worker test-fire and activity routes (basic/testfire.py)

Access rules (basic/auth.py and the boundary middleware):

  • Foreign browser origins are rejected.
  • Hostnames must be in HUGPY_CORE_ALLOWED_HOSTS.
  • HUGPY_CORE_AUTH_MODE=open (single operator, the default on a fresh install): trusted networks act without a key. From outside, a read needs a key of any scope and a change needs an admin key.
  • external mode: an admin session from the auth service at HUGPY_CORE_AUTH_BASE, proxied same-origin at /api/auth-svc/, or an admin API key.
  • A worker key (hpk_wk_…) is accepted only on POST /api/fleet/workers.

The console

The console is the original @hugpy/ui application shell. Its source of record is /srv/hugpy/app/ui/source. There, npm run build:basic builds dist-basic/, and scripts/bundle-basic.mjs copies it into hugpy_core/basic/static/, which ships as package data. The console tabs (Status, Compute, Models, API, Evictions, Metrics, Calls, Databases, Addons, Training and others) read core's derived tables. The console never computes a figure per row.

Every surface of the original UI stays visible. Whether a surface is available comes from GET /api/fleet/addons, which is mapped in the UI's src/basic/availability.jsx. A surface whose addon is only planned is shown as not installed, naming its provider, rather than hidden. On ae today, the installed addons are distribution, evaluations, media, video and fleet; the planned ones are planning, automation and toolserver. /media, /video and /fleet are the arms' own UIs, proxied as described above. --headless leaves the console out.

packages/hugpy_core/ui/ is an older divergent snapshot of the UI workspace, kept for reference. It is not a build source.

Postgres cluster

Core state lives in database hugpy_core, in core's own cluster:

  • The cluster is provisioned at install by the bundled toolserver: abstract-toolserver db install --system --account hugpy-core.
  • initdb runs under /var/lib/hugpy-core/pg. The system unit hugpy-core-postgres.service (Postgres 16) runs it as the service account.
  • It is socket only, with no TCP. The socket is named for port 7306. Clients authenticate with peer auth.
  • Connection settings are the five SOLCATCHER_POSTGRESQL_HOST/PORT/USER/NAME/PASS keys, which the toolserver writes in its fenced block in /etc/hugpy/core.env. hugpy_core.basic.database.conninfo_from_env reads them.
  • It holds registrations and private worker credentials, snapshots, the w_<table> mirrors of every wrapper table, the derived tables (model_catalog, model_workers, model_quants, model_metrics, eviction_events, worker_drives, fleet_copies, fleet_feasibility, central_copies, transfers, transfer_rates, model_grades, …), route_log and api_keys, queue tickets, worker keys, downloads, benchmark and training runs, and the management audit.
  • Core reads wrappers only over HTTP, never through their database files.

Package removal stops core and its cluster. apt purge removes the cluster, its data, the configuration and the service account.

Legacy app (not live)

The pre-0.2 runtime is still in the tree:

  • web/ (the Flask app, routes, console routes, DB viewer, install.sh)
  • registry/, relay/
  • brain/ (failure evaluation, planner, model groups, fix ladder)
  • calllog/, config/, discord/, help/, intake/, models/, schema/sql, testfire/
  • install/installer.py (the orbit installer)

To run it, you would need hugpy-core legacy … and the [legacy] extra: flask, abstract_flask, hugpy-storage, hugpy-engine, abstract_toolserver[server,db], hugpy-media and hugpy-video. Core's live venv has none of these, so the legacy app does not start there. Nothing on ae runs it. Basic mode imports only three pieces of it: grading.grader (the rubric and checkers), the constants in brain.intent, and the ARMS table in relay.arms.

Some ideas from the legacy design were lifted into basic mode:

  • the grader
  • test fire
  • the Hub downloader (from hugpy_storage)
  • the options resolver
  • the router's pick

The rest are not ported:

  • the brain's automatic evaluation of load failures
  • the venv fix ladder
  • model groups as routing
  • Discord
  • the Flask Help tickets

Layout

hugpy_core/
  basic/        LIVE: app (aiohttp routes, collector, copier, downloader, scanner), cli, arms (proxy),
                auth, database (Postgres), derive (derived tables), fleet, drives, downloads, scan,
                grades, eta, testfire, static/ (the console bundle)
  router/       LIVE: the folded router — core/ (decide, queue), links/ (box links), serve/ (proxy,
                manage, relay, keys, queue), observability/ (store + schema), imports/
  training/     LIVE: central's fine-tune (env, trainer, presets, runs; docs/ = training corpus)
  install/      cli.py (LIVE dispatcher), installer.py (legacy orbit installer)
  grading/      grader.py used by basic; the rest legacy
  imports/      shared constants / functions / classes (docs/packages.md#the-imports-layout-convention)
  web/ registry/ relay/ brain/ calllog/ config/ discord/ help/ intake/ models/ schema/ testfire/   legacy

Other files in this directory:

  • BASIC-CORE.md: the basic-core contract and its version-by-version record (0.2 to 0.4.40).
  • MIGRATION.md: the 0.1.70 package regrouping.
  • MIGRATION-imports.md: the imports/ layer.
  • docs/: the model-census note and the 2026-10-09 implementation ledger.

Build and install

Build packaging/debian/build.sh with Python 3.12. It wheels core, the sibling hugpy_wrapper and the sibling abstract_toolserver into /usr/share/hugpy-core/wheels. It checks that the router schema and the console's index.html made it into the wheel, and writes build/debian/hugpy-core_<version>_<arch>.deb. The deb depends on python3 (>= 3.12, << 3.13), python3-venv, systemd, postgresql and util-linux.

The postinst:

  1. creates /opt/hugpy-core/venv
  2. installs the bundled wheels offline
  3. uninstalls hugpy-router, which was folded into core at 0.4
  4. runs hugpy-core install --system

For install and operations procedures, see docs/install.md and docs/operations.md.

Tests are under tests/. conftest.py creates a throwaway Postgres cluster with initdb and one database per test. See docs/development.md.

Glossary: mirrored call-log columns

Core keeps the column names that hugpy_wrapper gives them. The box measures each value and the collector mirrors it into w_call_log. The values are read, never recomputed.

column meaning
box_id the source box's row id. Every box numbers its own rows from 1, so the mirror keys a row by (worker, box_id).
measured_vram_after_load the engine's GPU allocation at the ready or reuse boundary
process_bytes_after_call the engine's GPU memory after the call
non_expert_bytes estimated GPU weight bytes, including GPU-resident experts
tok_bytes_gpu / tok_bytes_ram weight bytes read per generated token, from GPU or from RAM
free_vram_before / free_vram_after budgetable GPU headroom before or after the call

Metadata

Release files for hugpy-core 0.4.41

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

Source distribution (sdist)

Source distribution for hugpy-core 0.4.41
File Size Uploaded
hugpy_core-0.4.41.tar.gz 5.6 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for hugpy-core 0.4.41
File Interpreter ABI Platform
hugpy_core-0.4.41-py3-none-any.whl Python 3 none any Details

Total release size: 11.1 MB

Release files / hugpy_core-0.4.41.tar.gz

Download URL hugpy_core-0.4.41.tar.gz
Size 5.6 MB
Tags Source
SHA-256 checksum
How to use checksums
8ec2468fb30db9083593b0642eabd9ec8d5645742cf25301f69e06df99adf76b
BLAKE2b-256 checksum
How to use checksums
e6c756817cb070f2a4103569176fae8e1cac5ffdb33cefbacd5286561e63c044
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release files / hugpy_core-0.4.41-py3-none-any.whl

Download URL hugpy_core-0.4.41-py3-none-any.whl
Size 5.6 MB
Tags Python 3
SHA-256 checksum
How to use checksums
36f527b7c621445d62df35615f85e20448ca761a0e487f8bdbf2fee506adb73a
BLAKE2b-256 checksum
How to use checksums
f1f34a2c8b075a92274346f13efced323caf9f48c147c1a0deba5367072d8d5f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release history Release notifications | RSS feed

0.4.46

2 release files

0.4.45

2 release files

0.4.44

2 release files

0.4.43

2 release files

0.4.42

2 release files

This release

0.4.41 This release

2 release files

0.4.28

2 release files

0.4.27

2 release files

0.4.26

2 release files

0.1.9

1 release file

0.1.8

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

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