Repo: https://github.com/celbridge-org/celbridge-hub-api-client
Client to connect to Celbridge-hub API for ZIPed package upload and download
- (see celbridge-hub package API server: https://github.com/celbridge-org/celbridge-hub)
v4 client — targets the v8 server (pages decoupled from packages). Authentication is via an API key; the old admin-login/password flow is gone. Pages are now published as their own ZIP uploads, independent of packages (see
TDDS/version 4 design document.md).
Create a .env with the server URL and your organisation API key:
BASE_URL=https://yourusername.pythonanywhere.com
API_KEY=kpf_xxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
The API key is issued once by your organisation admin (it is stored only as a
salted hash on the server, so it cannot be recovered later — keep it safe). Every
request the client makes sends Authorization: Api-Key <key>; there is no separate
login step. See .env.example for a template.
An interactive menu makes it easy to browse packages, upload new versions from the
files_to_upload/ directory, download to downloads/, manage aliases, and publish
web pages.
Single-file uploads (the old upload.py / /api/upload/) were removed in v7.
To upload, use the menu's Upload option or package_upload.py.
% python menu.py
Connected to https://yourusername.pythonanywhere.com with API key.
=== Packages API ===
1 - Packages (list / show / register / delete)
2 - Versions (list / detail / upload / download / tombstone)
3 - Aliases (list / set / remove)
4 - History (full / as-of-version)
5 - Pages (publish / list / detail / unpublish)
6 - Download latest version (GET /api/packages/{name}/latest)
7 - Upload a package (POST /api/packages/{name}/versions)
0 - Exit
Select option: Select option: 1
--- Packages ---
1 - List all packages (GET /api/packages)
2 - Show package metadata (GET /api/packages/{name})
3 - Register a new (empty) package (POST /api/packages)
4 - Delete entire package (DELETE /api/packages/{name})
0 - Back
Select option: 1
GET https://yourusername.pythonanywhere.com/api/packages/
Name Latest Vers Author Uploaded
------------------------------------------------------------------------------------------
fred-chess 2 2 popeye 2026-05-09 14:19:01
space-chess24 1 1 chris 2026-05-09 14:22:26
Select option: 2
...
Enter number (or press Enter to cancel): 1
GET https://yourusername.pythonanywhere.com/api/packages/fred-chess/
Package: fred-chess
Created: 2026-05-09 14:11:38
Ver Author Uploaded Tomb Summary
------------------------------------------------------------------------------------------
2 popeye 2026-05-09 14:19:01 added feature - should become version 2
1 mattilda 2026-05-09 14:11:38 forked to create new package fred-chess
Alias Version
------------------------------
latest 2In v8 pages are independent of packages. A page is its own ZIP upload whose
root is the served site, plus a pages.toml manifest naming the publish path:
[publish]
path = "dev/chess24"The bundle is served at /pages/<org-slug>/<path>/ (public, unauthenticated).
Re-uploading the same path replaces it; uploading, tombstoning, or deleting a
package never affects a page.
Select option: 5
--- Pages ---
(pages are independent of packages: each page is its own ZIP upload
containing a pages.toml with [publish].path; served at /pages/<org>/<path>/)
1 - Publish a page (upload ZIP) (POST /api/pages)
2 - List published pages (GET /api/pages)
3 - Show page detail (GET /api/pages/{path})
4 - Unpublish a page (DELETE /api/pages/{path})
0 - Back
Select option: 1
...
This bundle publishes to path: dev/chess24
POST https://yourusername.pythonanywhere.com/api/pages
Published page: dev/chess24
Page: https://yourusername.pythonanywhere.com/pages/acme/dev/chess24/package_upload.py uploads a single package ZIP (must contain package.toml with a
name):
python package_upload.py ./files_to_upload/mysite.zip --summary "my change"The tests are live integration tests — they run against the server in BASE_URL
using the API_KEY from .env (and skip if the server is unreachable or the key is
rejected):
pip install -r requirements-dev.txt
pytest -q