Skip to main content

scalio-platform

Shared building blocks of the Scalio apps (Nexus, Compass, Citadel, Foundry, Rosetta, Pulse, Beacon, Cockpit, ID, Monitor). Before this package each app kept its own copy of these files (architecture review C2/C4, ADR 0058 in scalio-docs). Nothing in this package is secret.

Module What it does
scalio_platform.job_lease Runs a background loop on one instance at a time (lease in db.job_leases). During an update two instances run side by side; without the lease both would act.
scalio_platform.migrations Schema migrations for MongoDB: ordered steps, each applied once, version in db.meta (schema_version).
scalio_platform.health The answer of GET /api/health that Scalio Monitor reads (ok / degraded / down, leases of the loops).
scalio_platform.error_report Sends unexpected errors to Scalio Monitor, only when ERROR_REPORT_URL and ERROR_REPORT_TOKEN are set.
scalio_platform.app_links Links and features of the product switcher (SCALIO_APP_LINKS, SCALIO_STAGE, SAAS_MODE).
scalio_platform.roles Permission catalog of the app, built-in and own roles (app_roles), require_perm for routes, shared role API (roles_router) for RoleAdmin in scalio-ui.
scalio_platform.tenant_jobs Runs one job per tenant side by side (for_each_tenant): at most TENANT_JOBS_PARALLEL at once (default 4), a time budget per tenant (TENANT_JOBS_TIMEOUT, default 900 s), a failing tenant never stops the others; for MSP instances with many customers (ADR 0063 mandant/M11).
scalio_platform.setup_wizard State of the Einrichtungsassistent (GET/PUT /api/setup/wizard, ADR 0060): wizard_state, save_wizard, parse_update.
scalio_platform.audit Protokoll → Aktionen (ADR 0070): one format in db.audit_log (record, target, diff), TTL 365 days, audit_router (GET /api/audit with filters, cursor and counts per chip, GET /api/audit/export.csv), migrate for old formats, uncovered for the route guard test.
scalio_platform.jobs Protokoll → Jobs (ADR 0070): Registry of the background jobs, run records each run (db.job_runs, TTL 90 days; a job returning None, 0, False, an empty list or a dict without summary and counts only updates db.job_state), «Jetzt ausführen» through db.job_requests and serve_requests in the worker, QueueSource for the app's queue, jobs_router.
scalio_platform.mail E-Mail (ADR 0069 W5): sending through SMTP or Microsoft 365 (secrets sealed by the app, never returned), outbox mail_outbox with retries (flush in the worker), templates per mail kind and language with placeholders (MailKind, own texts in settings {id: "mail_templates"}), router for «E-Mail-Versand» and «E-Mail-Vorlagen» in scalio-ui 0.29.0.

Use in an app

scalio-platform==0.1.0            # backend/requirements.txt, exact version
from scalio_platform import error_report, health, job_lease
from scalio_platform.migrations import BASELINE, run_migrations

MIGRATIONS = BASELINE                      # append (2, migrate_002_...) and so on, never edit past steps

async def startup():
    await run_migrations(db, MIGRATIONS)   # before the background loops
    for name, loop in LOOPS.items():
        asyncio.create_task(job_lease.run_exclusive(db, name, loop))

error_report.install(app, "citadel", build_info)
code, body = await health.health_report(db, build_info(), lease_jobs=sorted(LOOPS))

# probe of the provisioning engine (SaaS on the Docker engine), header X-Provision-Health-Secret
code, body = await health.provision_report(db, build_info(), secret=os.environ.get("PROVISION_HEALTH_SECRET", ""),
                                           provided=request.headers.get("x-provision-health-secret", ""))

A migration step must be safe to run twice: two instances can start at the same moment.

Zurücksetzen (factory reset, ADR 0064 / 0069)

from scalio_platform.factory_reset import factory_reset_router

async def after_wipe(remove_license: bool):
    if remove_license:
        await licensing.runtime.refresh(db)      # free edition right away
    await login_policy.announce_setup_code()     # new setup code in the log

app.include_router(factory_reset_router(
    get_db=lambda: db, current_user=require_admin,
    password_hash=lambda u: u.get("password_hash"), verify_password=auth.verify_password,
    keep_meta=(f"{PRODUCT.code}_installation", "schema_version"), license_meta=(PRODUCT.meta_id,),
    saas_mode=saas_mode, after_wipe=after_wipe))

GET /api/admin/factory-reset answers {available, password} (SaaS: not available, the customer portal resets it); POST takes {confirm: "RESET", password, remove_license}. allowed(user) narrows who may (Foundry: platform owner), error(code) maps the codes reset_in_portal, reset_confirm_wrong, current_password_wrong to the app's own errors. The frontend is FactoryReset from @scalio-cloud/ui 0.27.0.

Develop and release

  • pip install -e ".[dev]", then pytest -q and ruff check . (both run in CI).
  • Release: bump pyproject.toml and __version__, open a scalio-docs PR with python3 tools/changelog.py --repo scalio-platform release <version> (changelog in scalio-docs changelog/scalio-platform/, ADR 0066), merge, then «Freigeben» in the Scalio Cockpit (workflow publish.yml: PyPI with trusted publishing, tag and GitHub release). Tags without v.
  • A change of behaviour changes every app on its next upgrade: say so in the PR and the CHANGELOG.

Protokoll: Aktionen und Jobs (ADR 0070)

from scalio_platform import audit, jobs
from scalio_platform.audit import Category

CATEGORIES = (Category("access", "Zugriff", "Access", ("user.", "role.")),
              Category("license", "Lizenz", "License", ("license.",), always=True))

await audit.record(db, "user.create", user, category="access", target=audit.target("user", u["id"], u["email"]),
                   detail=f"Konto angelegt · {u['email']}", request=request)

app.include_router(audit.audit_router(get_db=lambda: db, viewer=require_perm(CATALOG, me, get_db, "audit.view"),
                                      categories=CATEGORIES, tenant_scope=lambda u: None), prefix="/api")

# worker
REGISTRY = jobs.Registry([jobs.Job("mail_intake", "Mail-Eingang", "Mail intake", "Jede Minute", "Every minute")])
await jobs.run(db, "mail_intake", mail_intake)            # inside the loop; return None when nothing happened
await jobs.serve_requests(db, REGISTRY, HANDLERS)         # own loop every few seconds («Jetzt ausführen»)

# API
app.include_router(jobs.jobs_router(registry=REGISTRY, get_db=lambda: db, viewer=view, runner=run_perm,
                                    queue=MyQueue(), audit=log), prefix="/api")

Call audit.ensure_indexes(db) and jobs.ensure_indexes(db) at startup. Never write passwords, tokens or contents into detail, changes or a job summary (changes drops fields named like pass/secret/token/key).

E-Mail (ADR 0069 W5)

from scalio_platform import mail

KINDS = (mail.MailKind("resolved", ("Ticket erledigt", "Ticket done"),
                       ("An die meldende Person, wenn ein Ticket erledigt ist", "To the reporter when a ticket is done"),
                       subject={"de": "[{key}] Erledigt: {title}", "en": "[{key}] Done: {title}"},
                       body={"de": "Guten Tag {name}\n\n...\n\n{sender}", "en": "Hello {name}\n\n...\n\n{sender}"},
                       tokens=("key", "title", "name", "link"), sample={"key": "IT-12", "title": "Drucker", ...},
                       switchable=True),)

mailer = mail.Mailer(get_db=lambda: db, app_name="Scalio Beacon", kinds=KINDS,
                     seal=secret_box.seal, unseal=secret_box.unseal,      # the app's own encryption
                     collection="meta",                                   # default "settings"
                     override=scalio_selftest.mail.smtp_settings,         # test mailbox on test instances
                     allowed=scalio_selftest.mail.allowed, base_url=lambda: os.environ.get("APP_PUBLIC_URL", ""))
app.include_router(mailer.router(admin=require_admin, audit=log), prefix="/api")   # /api/mail/...

await mailer.send(person["email"], "resolved", {"key": t["key"], ...}, lang=person.get("language"), ref=t["id"])
await jobs.run(db, "mail_outbox", mailer.flush)        # worker, every 30 seconds; call mailer.ensure_indexes() once

Every template knows {app_name}, {app_url} and {sender} (sender name of the settings, else the app name). A template may be plain text or HTML (from the editor); in HTML the values are escaped. The built-in kind test is the test mail. send returns False when mail is off, the kind is switched off, or allowed refuses the address. check(subject, html) may refuse a mail with ValueError (Foundry: no forms, only https links); it runs on saving or testing a template and before a mail goes into the outbox.

Metadata

Release files for scalio-platform 0.8.1

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-platform 0.8.1
File Size Uploaded Provenance Actions
scalio_platform-0.8.1.tar.gz 63.2 kB With provenance Details for scalio_platform-0.8.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for scalio-platform 0.8.1
File Interpreter ABI Platform Actions
scalio_platform-0.8.1-py3-none-any.whl Python 3 none any Details for scalio_platform-0.8.1-py3-none-any.whl

Total release size: 110.6 kB

Release files / scalio_platform-0.8.1.tar.gz

Download URL scalio_platform-0.8.1.tar.gz
Size 63.2 kB
Tags Source
SHA-256 checksum
How to use checksums
5f00cf2f7412e95b4ba677f28bd566fc9ac5bd2bba003289e6d331abf7d52c65
BLAKE2b-256 checksum
How to use checksums
f96531c51c5e99472710bfeae7e7b50024fc20325327f7d90aab1f4d714ab1be
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 Nov 2, 2026.

Transparency log

Release files / scalio_platform-0.8.1-py3-none-any.whl

Download URL scalio_platform-0.8.1-py3-none-any.whl
Size 47.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e3926ab080614fbdab700859a27419e7f27cfc65407bcae00418b2bd67136935
BLAKE2b-256 checksum
How to use checksums
0b3c0df423947a6cdc21bbee6fb319ec71fe94ca31f90760fff0fe5f1e3ec088
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 Nov 2, 2026.

Transparency log

Security for scalio-platform 0.8.1

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_platform-0.8.1.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 Nov 2, 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