halieum
A minimal, dependency-free license guard for personal Python projects.
import halieum
halieum.init("your-license-id")
import halieum does nothing on its own — no network, no file I/O, no
output, no monkey-patching. It cannot affect the surrounding code. All logic
runs inside init().
When the license for id is confirmed invalid (a validly-signed license
whose expiry has passed, or one that was tampered with / forged), halieum
enforces from the project root. When the license server is merely
unreachable, it never deletes anything: it falls back to a local cache, and
on a first-ever run with no cache it simply exits the host program.
halieum is a deterrent for the author's own projects. Anyone who can edit the source can delete the two lines above; treat it as a speed bump, not a vault.
How it works
init(id)
-> GET <PAGES_BASE>/licenses/<id>.json (GitHub Pages)
-> verify RSA (PKCS#1 v1.5 + SHA-256) with the embedded PUBLIC key
-> compare exp against the local UTC clock
valid & current -> cache to temp dir, return silently
expired / tampered -> ENFORCE (delete) then exit
-> if the network failed:
cache present & valid -> return silently
cache present & expired -> ENFORCE (delete) then exit
no cache (first run) -> exit host program (never delete)
The signature is asymmetric on purpose: the public key ships inside the package (safe to be public on PyPI — it can only verify), while the private key stays in a GitHub Actions secret and signs each license.
Install
pip install halieum
Runtime requires the standard library only and targets Python 3.6+.
init() API
halieum.init(
id, # REQUIRED. License identifier issued by the Action.
dry_run=False, # Only determine validity; never delete, never exit.
debug=False, # Log to sys.stderr (never print). Default: silent.
mode="recursive", # "recursive" | "source" | "self" | "file"
target=None, # Dir/file to enforce on. Default: project root.
root=None, # Override project-root auto-detection.
extensions=None, # Custom extension filter (implies source filtering).
timeout=None, # Network timeout in seconds (default 5.0).
exit_after=True, # After a destructive enforcement, exit the process.
)
Modes
| mode | effect |
|---|---|
recursive (default) |
Delete every file under target (default: project root), then remove emptied directories. .git/.hg/.svn are preserved. |
source |
Like recursive, but only files whose extension is in extensions (default: common source extensions). |
self |
Delete only the single file that called init(). |
file |
Delete only the single resolved target file. |
Project root is the entry-script directory (__main__.__file__, else
sys.argv[0]), falling back to the current working directory. So calling
init() from a sub-module still resolves to the project root — never the
sub-module folder and never the installed package folder.
Examples
# Check only, change nothing, print diagnostics:
halieum.init("my-id", dry_run=True, debug=True)
# On failure, delete only the ./src tree:
halieum.init("my-id", target="src")
# On failure, delete only source files, keep data/assets:
halieum.init("my-id", mode="source")
# On failure, delete only the file that calls init():
halieum.init("my-id", mode="self")
Output never uses print (which a host app can hijack); diagnostics go
straight to sys.stderr and are silent unless debug=True.
Setting up your own license server (one time)
The signing key, the issuing Action and the Pages distribution all live in this repository.
1. Generate the RSA keypair
pip install pycryptodome
python tools/gen_keys.py
This rewrites src/halieum/_keys.py with your public key and prints the
private key (PEM) to stdout.
2. Store the private key as a secret
Repo → Settings → Secrets and variables → Actions → New repository secret:
- Name:
HALIEUM_SIGNING_KEY - Value: the PEM private key printed above
Until a real public key is generated,
_keys.pyships a placeholder (n = 0) and halieum stays inert (it reports "unconfigured" and never enforces).
3. Enable GitHub Pages
Repo → Settings → Pages → Source: GitHub Actions.
Set your Pages base URL in src/halieum/_config.py:
DEFAULT_BASE = "https://<your-username>.github.io/<repo>/"
Licenses are then served at <DEFAULT_BASE>licenses/<id>.json.
4. Issue a license
Repo → Actions → Issue License → Run workflow, and provide:
id— the license identifier (the same string passed toinit()).datetime— expiry, ISO-8601 UTC, e.g.2026-12-31T23:59:59Z.
The workflow signs the license, writes licenses/<id>.json, commits it back as
the source of truth, and deploys all licenses to Pages. Re-running with the
same id updates its datetime and re-signs.
Safety guarantees
halieum is deliberately biased toward not deleting:
- A network failure alone never deletes. It uses the cache, or (first run) exits the host program.
- Deletion happens only for a provably invalid license: a correctly signed license past its expiry, or one whose signature/id does not verify.
- The enforcement target is screened against a deny-list of filesystem roots, critical OS directories, the running interpreter, site-packages and halieum's own package directory. A target outside the project root is refused.
.git/.hg/.svnare preserved; each file removal is individually guarded (locked files on Windows are skipped, not fatal).- Any internal halieum error results in a no-op, never a deletion.
dry_run=Truedisables every destructive action and every exit.import halieumis inert; nothing runs untilinit()is called, and the sameidis only ever processed once per process.
Development
pip install -e ".[dev]" # editable install + pytest/build/twine
pytest -q # run the suite (stdlib only; no crypto needed)
python -m build # sdist + wheel
twine check dist/*
The test-suite signs with a throwaway RSA key embedded in
tests/conftest.py using pure Python, so it needs no third-party crypto. CI
(.github/workflows/ci.yml) runs the matrix across Python 3.8–3.14 on Ubuntu
and Windows.
Publishing to PyPI
python -m build
twine upload dist/*
License
MIT — see LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file halieum-0.1.0.tar.gz.
File metadata
- Download URL: halieum-0.1.0.tar.gz
- Upload date:
- Size: 22.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c3aae907fe5c7ce416346105a5e43ee2d9976bbef220b9bf0b5fa1eb41e6d010
|
|
| MD5 |
4fb35d35469cdfe26fd5f44639419db6
|
|
| BLAKE2b-256 |
e1b6d29a41ed8c519eabc3e102d76b45af1fabdb2ed9670443c9c2c07bf5df93
|
Provenance
The following attestation bundles were made for halieum-0.1.0.tar.gz:
Publisher:
python-publish.yml on Randark-JMT/halieum
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
halieum-0.1.0.tar.gz -
Subject digest:
c3aae907fe5c7ce416346105a5e43ee2d9976bbef220b9bf0b5fa1eb41e6d010 - Sigstore transparency entry: 2699126360
- Sigstore integration time:
-
Permalink:
Randark-JMT/halieum@6375de6b78691f1f3c443bcbb28e7c8f40d86d15 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Randark-JMT
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@6375de6b78691f1f3c443bcbb28e7c8f40d86d15 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file halieum-0.1.0-py3-none-any.whl.
File metadata
- Download URL: halieum-0.1.0-py3-none-any.whl
- Upload date:
- Size: 18.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7da8bf3d40449fd4ea459220c44a04044e961826790275c3c96afc740376b888
|
|
| MD5 |
b040ddc896e4302907ca45f0a44483f8
|
|
| BLAKE2b-256 |
4627f5d6829046f38ac998e42488db4fa0be148644139047e0a7859332400e94
|
Provenance
The following attestation bundles were made for halieum-0.1.0-py3-none-any.whl:
Publisher:
python-publish.yml on Randark-JMT/halieum
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
halieum-0.1.0-py3-none-any.whl -
Subject digest:
7da8bf3d40449fd4ea459220c44a04044e961826790275c3c96afc740376b888 - Sigstore transparency entry: 2699126407
- Sigstore integration time:
-
Permalink:
Randark-JMT/halieum@6375de6b78691f1f3c443bcbb28e7c8f40d86d15 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Randark-JMT
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@6375de6b78691f1f3c443bcbb28e7c8f40d86d15 -
Trigger Event:
workflow_dispatch
-
Statement type: