Skip to main content

scalio-license

Shared license check for the Scalio apps (Nexus, Citadel, Foundry, Rosetta; Compass later). Offline only: Ed25519 signatures against public keys embedded in each app, no call home, no GitHub. Concept and decisions L1-L9: https://claude.ai/artifact/UGD8mKguzvivMMJbKW1qEx

Nothing in this package is secret. The private seeds stay in 1Password and in the Compass License Managers.

How it works

  • Key: <payload_b64>.<sig_b64>, Ed25519 over the payload bytes (same format as Compass issues). Every key carries product_code (scalio-citadel, ...). An app accepts only its own code.
  • Own key per product, embedded in the app (TrustedKey). No environment override. Without an embedded key nothing is enforced, so the code can ship before the key exists.
  • Staging keys: Compass dev/test sign with their own key, embedded with plans=("nfr",). Such a key only issues base licenses of those plans and no add-ons, so a leaked staging seed never yields a sellable license.
  • Binding: self-hosted keys carry installation = the installation ID the app shows on its license page (random, created on first start, stored in db.meta). SaaS keys carry domains. A mismatch means Community plus an error. Compass lets a customer generate the key once; moving needs Scalio.
  • Purchase plus maintenance: exp is the end of maintenance. A build released after exp + 30 days makes the configuration read-only (423). Features and data stay. Trials (type: trial) end hard.
  • Limits only on create (402), existing data is never locked or deleted. Features off: 402.
  • SaaS (SAAS_MODE=true): the license page is read-only, Compass pushes the key through POST /api/provision/license signed with its control key K3 (PROVISION_PUBLIC_KEY).

Use in an app

from scalio_license import Addon, Edition, GateMiddleware, LicenseRuntime, Product, RequestGate, TrustedKey
from scalio_license.web import license_router, provision_license_router

PRODUCT = Product(
    code="scalio-citadel", name="Scalio Citadel", lang="en",
    trusted_keys=(),  # TrustedKey("cit2026a", "<public key>") once Markus made the key
    editions={"community": Edition("Community", frozenset(), {"tenants": 1, "admins": 1}),
              "pro": Edition("Pro", frozenset({"self_healing"}), {"tenants": 25, "admins": 5}), ...},
    limit_kinds=("tenants", "admins"), features=("self_healing", ...),
    claim_map={"maxAdmins": "admins", "maxEmployees": "tenants"})

runtime = LicenseRuntime(PRODUCT, usage={"tenants": count_tenants, "admins": count_admins},
                         public_url=get_public_url)
app.include_router(license_router(runtime, lambda: db, require_admin), prefix="/api")
app.include_router(provision_license_router(runtime, lambda: db), prefix="/api")
app.add_middleware(GateMiddleware, gate=RequestGate(runtime, feature_rules=[...], config_writes=[...],
                                                     exempt=[r"^/api/license", r"^/api/provision", r"^/api/auth"]))
# startup: await runtime.refresh(db); asyncio.create_task(runtime.refresh_loop(db))
# on create: await runtime.assert_can_add(db, "tenants")

Env per app (prefix from the product code): <APP>_LICENSE_KEY, <APP>_LICENSE_ADDONS (only used while nothing is stored in the database), APP_BUILD_DATE or BUILD_DATE (set in the image), APP_PUBLIC_URL, SAAS_MODE, PROVISION_PUBLIC_KEY.

Vendor tool

tools/scalio_license_tool.py is not part of the package. It makes key pairs and signs Scalio's own licenses (vendor, NFR) before a Compass License Manager exists.

python tools/scalio_license_tool.py genkey --out ~/citadel-seed.txt --kid cit2026a
python tools/scalio_license_tool.py base --seed-file ~/citadel-seed.txt --product scalio-citadel \
    --plan nfr --customer "Scalio Dev" --domains dev.scaliocitadel.com --days 365

Development

python3 -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/pytest && .venv/bin/ruff check .

tests/vector.json is a fixed key signed like Compass signs. Compass tests the same vector, so both stay in step.

Release

Bump version in pyproject.toml and __init__.py, add a CHANGELOG section, merge, then "Freigeben" in the Scalio Dev cockpit (or Actions -> Publish -> Run workflow on main with the version, or push a tag without v like 0.1.0). publish.yml tests, builds and publishes to PyPI with trusted publishing (no token); when started from the cockpit it also creates the tag and GitHub release. Apps pin the exact version and upgrade in their own PRs.

Metadata

Release files for scalio-license 0.2.0

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

Source distribution (sdist)

Source distribution for scalio-license 0.2.0
File Size Uploaded Provenance Actions
scalio_license-0.2.0.tar.gz 23.1 kB With provenance Details for scalio_license-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for scalio-license 0.2.0
File Interpreter ABI Platform Actions
scalio_license-0.2.0-py3-none-any.whl Python 3 none any Details for scalio_license-0.2.0-py3-none-any.whl

Total release size: 42.9 kB

Release files / scalio_license-0.2.0.tar.gz

Download URL scalio_license-0.2.0.tar.gz
Size 23.1 kB
Tags Source
SHA-256 checksum
How to use checksums
483e1af81673f64b217bcd38c985cbc0e99a78e51b0d2502bd53d74f4e08d50c
BLAKE2b-256 checksum
How to use checksums
24221bc516a16e5340ee587bc68c4a36b75eeb6d4ab1d35113fcefba40b821dc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.

Transparency log

Release files / scalio_license-0.2.0-py3-none-any.whl

Download URL scalio_license-0.2.0-py3-none-any.whl
Size 19.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c7b807b91b55857233971cb741fa6666f891103c493b20257e435f3912ce2825
BLAKE2b-256 checksum
How to use checksums
e136b0e396e4548326d90ebbfd6412e51b8e900aab177cbd46072b45c63eea18
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.

Transparency log

Security for scalio-license 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for scalio_license-0.2.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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