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.1.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.1.0
File Size Uploaded Provenance Actions
scalio_license-0.1.0.tar.gz 22.6 kB With provenance Details for scalio_license-0.1.0.tar.gz

Built distribution (wheel)

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

Total release size: 42.3 kB

Release files / scalio_license-0.1.0.tar.gz

Download URL scalio_license-0.1.0.tar.gz
Size 22.6 kB
Tags Source
SHA-256 checksum
How to use checksums
f09792978da16bf81f3e26e3e44274a91f31dbb7238f14bf8a29cbd7000b11ac
BLAKE2b-256 checksum
How to use checksums
03d3eeb9a283ba5a7999dea82fe7876c22e8489ab5822dc82713cfbd243012c8
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 26, 2026.

Transparency log

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

Download URL scalio_license-0.1.0-py3-none-any.whl
Size 19.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
073d54d55cb3d6f086d39e5908289ec2db8d792bbaa523e9841b2c89617f63a2
BLAKE2b-256 checksum
How to use checksums
1004b6c1eb93ed77acc7150b5fef77b0da2c838a311ce3e4f07d6c72389ad2af
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 26, 2026.

Transparency log

Security for scalio-license 0.1.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.1.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 26, 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