From c2d3c568a9b721af5f148045136cce4914168240 Mon Sep 17 00:00:00 2001
From: Stefano Noferi
Date: Wed, 7 Oct 2026 08:06:53 +0200
Subject: [PATCH 1/4] docs: announce the admina package name of 0.14.0
docs/guides/package-name.md describes the change of 0.14.0: Admina is
published as admina, and admina-framework ends with a 0.14.0 that stops
the installation with the commands to switch. The guide gives the
releases and package names, the upgrade with pip, uv and dependency
lists, the plugin pyproject line, and the --force-reinstall that restores
the module when admina-framework is uninstalled after admina was
installed over it.
The README install section, the mkdocs navigation, the [Unreleased]
notes and the 0.14.0 roadmap entry point to the guide.
---
CHANGELOG.md | 8 ++++
README.md | 14 +++++--
ROADMAP.md | 2 +
docs/guides/package-name.md | 83 +++++++++++++++++++++++++++++++++++++
mkdocs.yml | 1 +
5 files changed, 104 insertions(+), 4 deletions(-)
create mode 100644 docs/guides/package-name.md
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8b1f4ad..c2c557b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,14 @@ stability commitment. See [ROADMAP.md](ROADMAP.md) for planned milestones.
## [Unreleased]
+### Notes
+
+- From 0.14.0, Admina is published on PyPI as `admina`, and
+ `admina-framework` is no longer released after a final 0.14.0 that stops
+ the installation with the commands to switch. The module, the `admina`
+ command and the extras do not change. `docs/guides/package-name.md`
+ describes the upgrade.
+
## [0.13.2] — 2026-10-07
Patch release: the PyPI package `admina`, an alias of `admina-framework`,
diff --git a/README.md b/README.md
index a586772..e2ed98c 100644
--- a/README.md
+++ b/README.md
@@ -117,6 +117,12 @@ report = kit.gap_analysis(risk_category="high", current_compliance={...})
### Install from PyPI
+> **Package name change in 0.14.0.** From 0.14.0, Admina is published as
+> `admina`, and `admina-framework` is no longer released (a breaking change
+> for anything that installs `admina-framework`). The module, the `admina`
+> command and the extras do not change. See
+> [The `admina` package name](https://github.com/admina-org/admina/blob/main/docs/guides/package-name.md) for the steps to upgrade.
+
```bash
# Recommended for new users: SDK + proxy + dashboard.
# Lets you run `admina dev` and see the dashboard out of the box.
@@ -174,10 +180,10 @@ pip install admina-framework
> matters more than that extra coverage. See
> [Performance](#performance--hybrid-python--rust-engine) for the trade-off.
-The PyPI package `admina` is an alias: `pip install admina` installs
-`admina-framework` at the same version, and `admina[X]` installs
-`admina-framework[X]` for every extra above. It has no code of its own, so
-both names install the same `admina` module.
+Up to 0.13.x the PyPI package `admina` is an alias: `pip install admina`
+installs `admina-framework` at the same version, and `admina[X]` installs
+`admina-framework[X]` for every extra above. From 0.14.0 `admina` holds the
+code (see [The `admina` package name](https://github.com/admina-org/admina/blob/main/docs/guides/package-name.md)).
### Or install from source
diff --git a/ROADMAP.md b/ROADMAP.md
index 917f037..b16baeb 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -134,6 +134,8 @@ deployment, and a forensic store that can be verified record by record.
Beyond the EU AI Act.
+- Package name: Admina is published as `admina` (breaking for installs of
+ `admina-framework`; see `docs/guides/package-name.md`)
- NIS2 template (incident response, supply-chain obligations)
- ISO / IEC 42001 template (AI management system controls)
- SOC 2 template (Trust Services Criteria mapping)
diff --git a/docs/guides/package-name.md b/docs/guides/package-name.md
new file mode 100644
index 0000000..1e2319c
--- /dev/null
+++ b/docs/guides/package-name.md
@@ -0,0 +1,83 @@
+# The `admina` package name
+
+From **0.14.0**, Admina is published on PyPI as **`admina`**. The package
+`admina-framework`, which held the code up to 0.13.x, is no longer released
+after that. This is a breaking change for anything that installs
+`admina-framework`.
+
+What does not change: the Python module (`import admina`), the `admina`
+command, the extras, the Rust accelerator `admina-core` and the container
+images (`ghcr.io/admina-org/admina-proxy`, `ghcr.io/admina-org/admina-dashboard`).
+
+## Releases and package names
+
+| Releases | `admina-framework` | `admina` |
+|---|---|---|
+| up to 0.13.1 | the code | — |
+| 0.13.2 – 0.13.x | the code | an alias: no code, it installs `admina-framework` at the same version |
+| from 0.14.0 | 0.14.0 only: a final release that cannot be installed (see below) | the code |
+
+`admina-framework` 0.14.0 is a source distribution that stops the
+installation with this message, and leaves the installed packages as they
+are:
+
+```text
+admina-framework is now published as admina.
+Run: pip uninstall -y admina admina-framework && pip install admina
+```
+
+So `pip install -U admina-framework`, or a requirement `admina-framework`
+without an upper bound, fails from 0.14.0 on. A pin such as
+`admina-framework==0.13.2` or `admina-framework<0.14` keeps installing the
+0.13 releases.
+
+## How to upgrade
+
+Both packages contain the `admina` module. Remove the installed ones first,
+then install `admina`:
+
+```bash
+pip uninstall -y admina admina-framework
+pip install "admina[proxy]" # the extras you used: same names
+```
+
+With uv:
+
+```bash
+uv pip uninstall admina admina-framework
+uv pip install "admina[proxy]"
+# in a uv project: uv remove admina-framework && uv add "admina[proxy]"
+```
+
+In `requirements.txt`, `pyproject.toml` or any other dependency list,
+replace `admina-framework` with `admina` and keep the extras and the
+version constraint:
+
+```diff
+- admina-framework[proxy,rust]>=0.13,<0.14
++ admina[proxy,rust]>=0.14,<0.15
+```
+
+A plugin created with `admina plugin create` depends on `admina-framework`
+in its `pyproject.toml`: change that line in the same way.
+
+Check the result:
+
+```bash
+python -c "import admina; print(admina.__version__)" # 0.14.0 or later
+pip list | grep -i admina # admina (and admina-core with [rust])
+```
+
+## If `import admina` fails after the upgrade
+
+Installing `admina` 0.14 while `admina-framework` 0.13 is installed, and
+then uninstalling `admina-framework`, removes the files of the `admina`
+module: pip and uv delete every file that the uninstalled package had
+installed, and both packages installed the same files. `pip list` still
+shows `admina`, but `import admina` raises `ModuleNotFoundError`. Reinstall
+it:
+
+```bash
+pip install --force-reinstall "admina[proxy]"
+# or: uv pip install --reinstall "admina[proxy]"
+```
diff --git a/mkdocs.yml b/mkdocs.yml
index 3cd95a9..fe096ff 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -51,6 +51,7 @@ nav:
- Integrations: guides/integrations.md
- Air-gapped deployment: guides/airgapped-deployment.md
- Upgrading to 0.13: guides/upgrade-0.13.md
+ - The admina package name: guides/package-name.md
- SDK Reference:
- Overview: api/sdk.md
- GovernedModel: api/governed-model.md
From d2908a7ac0b7abbd1db07d42d53d175b1962898e Mon Sep 17 00:00:00 2001
From: Stefano Noferi
Date: Wed, 7 Oct 2026 16:40:22 +0200
Subject: [PATCH 2/4] docs(roadmap): keep 0.14.0 to the package name
The 0.14.0 entry lists the change of the PyPI package name to admina.
The sections after it (not yet scheduled, 1.0.0, 1.1.0, 2.0.0) are
removed; the roadmap lists the released milestones and the next release.
---
ROADMAP.md | 89 +++++-------------------------------------------------
1 file changed, 8 insertions(+), 81 deletions(-)
diff --git a/ROADMAP.md b/ROADMAP.md
index b16baeb..ff8025e 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -130,87 +130,14 @@ deployment, and a forensic store that can be verified record by record.
---
-## 0.14.0 — Compliance template expansion
-
-Beyond the EU AI Act.
-
-- Package name: Admina is published as `admina` (breaking for installs of
- `admina-framework`; see `docs/guides/package-name.md`)
-- NIS2 template (incident response, supply-chain obligations)
-- ISO / IEC 42001 template (AI management system controls)
-- SOC 2 template (Trust Services Criteria mapping)
-- Cross-framework gap analysis: surface obligations that satisfy
- multiple frameworks in a single control
-- Report export in PDF and DOCX, signed with the forensic key
-
----
-
-## Not yet scheduled
-
-Planned, without a release assigned yet.
-
-**Observability**
-
-- Emission of the streaming-request metadata shaped in 0.11 onto the
- OpenTelemetry GenAI semantic conventions (`gen_ai.request.model`,
- `gen_ai.usage.*`, `gen_ai.response.finish_reasons`,
- `gen_ai.client.operation.duration`) — no metadata field is renamed at
- the emission boundary.
-- An SLO panel on the Prometheus metrics.
-- Structured error taxonomy: stable error codes across proxy, SDK, and
- REST API.
-- Benchmark regression gate in CI (fail on a >10% regression versus
- baseline).
-- Tracing correlation from the SDK through the proxy to the upstream LLM
- (the gateway already records W3C trace context).
-
-**Multi-tenancy and RBAC** — operating Admina as a shared service.
-
-- Organisation / workspace isolation in the proxy
-- Per-tenant quotas (request rate, forensic retention, compliance
- templates enabled)
-- Role-based access control on proxy endpoints (read / write / admin)
-- OIDC authentication provider as a built-in plugin
-- Shared async pool for data connectors; decision cache for the injection
- firewall fast path.
-- Per-tenant forensic namespace with independent hash chains
-
----
-
-## 1.0.0 — API freeze and long-term support
-
-The stability commitment. Shipped when the public surface has settled
-through real-world use.
-
-- Plugin ABI v1 frozen with contract tests; third-party plugin
- certification suite
-- Official client SDKs: TypeScript, Go
-- Deprecation policy formalised; 18-month LTS window for the 1.0 line
-- Security advisory process documented; CVE assignment workflow
-- Removal of the pre-0.3.0 compatibility shims in `proxy/`
-
----
-
-## 1.1.0 — Horizontal scale
-
-Running Admina as stateless, horizontally-scaled infrastructure.
-
-- Stateless proxy mode backed by shared Redis / ClickHouse
-- Hot-reload of governance configuration without restart
-- Official Helm chart and Kubernetes operator
-- Cross-region federation for the forensic store
-
----
-
-## 2.0.0 — Only if required
-
-Reserved for breaking changes that cannot be delivered under 1.x.
-No breaking changes are planned. Candidate drivers:
-
-- Plugin ABI v2 if real-world use reveals design limitations that cannot be
- extended under v1
-- Streaming-first pipeline rearchitecture if chunk-level governance
- becomes the dominant workload
+## 0.14.0 — Package name
+
+- Admina is published on PyPI as `admina`; `admina-framework` is no longer
+ released after a final 0.14.0 that stops the installation with the
+ commands to switch. A declared breaking change for installs of
+ `admina-framework`: the module, the `admina` command and the extras do
+ not change. See
+ [docs/guides/package-name.md](docs/guides/package-name.md).
---
From 70e4d12221a56d571ab2a36181d119e4d4415088 Mon Sep 17 00:00:00 2001
From: Stefano Noferi
Date: Wed, 7 Oct 2026 16:40:22 +0200
Subject: [PATCH 3/4] docs: simplify the licence row and four descriptions
The README licence row reads Apache 2.0. The init wizard docstring, the
GDPR and NIS2 module docstrings and the MODEL_CARD compliance limits
describe what the code does. The README and CONTRIBUTING links to the
roadmap describe its new content.
---
CONTRIBUTING.md | 4 ++--
MODEL_CARD.md | 2 +-
README.md | 4 ++--
admina/cli/main.py | 3 +--
admina/domains/compliance/gdpr.py | 2 +-
admina/domains/compliance/nis2.py | 2 +-
6 files changed, 8 insertions(+), 9 deletions(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index dc6f2f4..5065235 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -231,8 +231,8 @@ maintainers before it is merged.
### Areas Where Help Is Welcome
-See [ROADMAP.md](ROADMAP.md) for the planned direction from 0.9.x to 1.0
-and beyond. In addition, the following areas accept contributions on an
+See [ROADMAP.md](ROADMAP.md) for the released milestones and the next
+planned release. In addition, the following areas accept contributions on an
ongoing basis regardless of the current milestone:
- New injection detection patterns (`admina/domains/agent_security/firewall.py`)
diff --git a/MODEL_CARD.md b/MODEL_CARD.md
index 35195f1..b5218fa 100644
--- a/MODEL_CARD.md
+++ b/MODEL_CARD.md
@@ -939,7 +939,7 @@ to `minimal`.
- **No coverage of national implementing legislation** beyond EU. Member
states may enact additional obligations (e.g. on biometric
identification by law enforcement). Admina does not model these.
-- **ISO/IEC 42001 and SOC 2** are not implemented in OSS.
+- **ISO/IEC 42001 and SOC 2** are not implemented.
---
diff --git a/README.md b/README.md
index e2ed98c..33a2868 100644
--- a/README.md
+++ b/README.md
@@ -80,7 +80,7 @@
| Audit trail | logs you hope nobody deletes | **SHA-256 hash chain** — tamper-evident by design |
| Adding governance to existing code | rewrite the call sites | **Zero code changes** via proxy, or 3 lines via SDK |
| Performance overhead | unknown | **Measured** — engine microbenchmarks and a gateway benchmark to run on your hardware ([Performance](#performance--hybrid-python--rust-engine)) |
-| License | varies | **Apache 2.0**, open core |
+| License | varies | **Apache 2.0** |
> Admina is **decision-support and defense-in-depth**, not legal advice. See [Compliance scope](#compliance-scope) for the full disclaimer and limitations.
@@ -1940,7 +1940,7 @@ guard error under `ADMINA_GUARD_FAIL_MODE=closed`) or `response_blocked`
- [CONTRIBUTING.md](https://github.com/admina-org/admina/blob/main/CONTRIBUTING.md) — development setup, testing, and pull request workflow
- [MODEL_CARD.md](https://github.com/admina-org/admina/blob/main/MODEL_CARD.md) — transparency artifact for every Admina governance component (intended use, scope, limitations, known failure modes), aligned with EU AI Act Art. 13 and NIST AI RMF
-- [ROADMAP.md](https://github.com/admina-org/admina/blob/main/ROADMAP.md) — planned milestones from 0.9.x to 1.0 and beyond
+- [ROADMAP.md](https://github.com/admina-org/admina/blob/main/ROADMAP.md) — released milestones and the next planned release
- [CHANGELOG.md](https://github.com/admina-org/admina/blob/main/CHANGELOG.md) — release notes
- [SECURITY.md](https://github.com/admina-org/admina/blob/main/SECURITY.md) — coordinated disclosure policy
- [CODE_OF_CONDUCT.md](https://github.com/admina-org/admina/blob/main/CODE_OF_CONDUCT.md) — Contributor Covenant 2.1
diff --git a/admina/cli/main.py b/admina/cli/main.py
index ff2c1c8..3d0588a 100644
--- a/admina/cli/main.py
+++ b/admina/cli/main.py
@@ -1736,8 +1736,7 @@ def configure(output: Path, non_interactive: bool) -> None:
to disable, loop-breaker thresholds, PII categories.
Defaults are restrictive: enforce mode, all firewall categories on,
- EU-aware PII set on. The wizard never recommends a "Pro" upgrade
- or a paid feature — the OSS edition is fully usable as-is.
+ EU-aware PII set on.
"""
if output.exists():
diff --git a/admina/domains/compliance/gdpr.py b/admina/domains/compliance/gdpr.py
index dd7b286..2977d2a 100644
--- a/admina/domains/compliance/gdpr.py
+++ b/admina/domains/compliance/gdpr.py
@@ -23,7 +23,7 @@
this release.
- :func:`render_dpia_template` — Art. 35 DPIA scaffold rendered
- as Markdown from the operator's input. The OSS module produces
+ as Markdown from the operator's input. The module produces
a *blank template populated with the operator's facts* — it is
NOT a guided wizard, it does NOT score or recommend mitigations,
and it is NOT legal advice. A real DPIA always involves the DPO
diff --git a/admina/domains/compliance/nis2.py b/admina/domains/compliance/nis2.py
index a75e93f..afc4323 100644
--- a/admina/domains/compliance/nis2.py
+++ b/admina/domains/compliance/nis2.py
@@ -18,7 +18,7 @@
(EU) 2022/2555) Art. 21 cybersecurity risk-management measures and
Art. 23 incident reporting.
-Scope of this OSS module: a *triage tool*. It enumerates the ten
+Scope of this module: a *triage tool*. It enumerates the ten
measure areas required by Art. 21 and lets the operator declare for
each one which of a small number of standard controls is in place,
producing a coverage score and a list of gaps.
From cd61c86b69c943cd5fde07215e9bc4199dba5127 Mon Sep 17 00:00:00 2001
From: Stefano Noferi
Date: Wed, 7 Oct 2026 16:53:04 +0200
Subject: [PATCH 4/4] =?UTF-8?q?chore:=20release=200.14.0=20=E2=80=94=20the?=
=?UTF-8?q?=20distribution=20is=20named=20admina?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The PyPI distribution of the framework is admina (pyproject name, the
full, adapters and all extras, uv.lock). The module, the admina command,
the extras and admina-core keep their names; the [rust] extra requires
admina-core>=0.14.0,<0.15.
admina-framework ends with 0.14.0: packaging/admina-framework holds a
setup.py that stops the installation with the commands to switch, and
scripts/build-admina-framework-sdist.py builds it as a source
distribution (Metadata-Version 2.4) with the version of pyproject.toml.
release.yml publishes admina, and on the v0.14.0 tag the admina-framework
sdist; the alias package of 0.13.2 is removed. tests/test_admina_framework_sdist.py
checks the archive, its metadata and the message.
Install hints in error messages, warnings, the plugin template
(admina>=), the README, the docs, MODEL_CARD, CONTRIBUTING,
core-rust/README.md and .env.example name admina. scripts/check-versions.py
reads the admina entry of uv.lock. The Markdown compliance report of the
proxy ends with the forensic section.
Version 0.14.0 in the eight manifests; CHANGELOG [0.14.0], ROADMAP and
SECURITY (0.14.x supported) updated.
---
.env.example | 2 +-
.github/workflows/release-core.yml | 2 +-
.github/workflows/release.yml | 52 ++++++-----
CHANGELOG.md | 41 +++++++--
CONTRIBUTING.md | 2 +-
MODEL_CARD.md | 6 +-
README.md | 65 ++++++-------
ROADMAP.md | 2 +-
SECURITY.md | 4 +-
admina/__init__.py | 2 +-
admina/cli/main.py | 10 +-
admina/cli/templates/plugin_pyproject.toml.j2 | 4 +-
admina/domains/data_sovereignty/pii.py | 2 +-
admina/engines/__init__.py | 4 +-
admina/engines/presidio.py | 4 +-
admina/integrations/_engines.py | 4 +-
admina/integrations/crewai/README.md | 2 +-
admina/integrations/langchain/README.md | 2 +-
admina/plugins/builtin/adapters/anthropic.py | 4 +-
admina/plugins/builtin/adapters/bedrock.py | 4 +-
admina/plugins/builtin/adapters/gemini.py | 4 +-
admina/plugins/builtin/adapters/mistral.py | 4 +-
admina/proxy/main.py | 12 +--
admina/redteam/__init__.py | 2 +-
core-rust/Cargo.lock | 2 +-
core-rust/Cargo.toml | 2 +-
core-rust/README.md | 2 +-
core-rust/pyproject.toml | 2 +-
core-rust/uv.lock | 2 +-
docs/guides/airgapped-deployment.md | 6 +-
docs/guides/package-name.md | 4 +-
docs/index.md | 11 ++-
packaging/admina-framework/README.md | 16 ++++
packaging/admina-framework/setup.py | 23 +++++
packaging/admina/README.md | 15 ---
packaging/admina/pyproject.toml | 69 --------------
pyproject.toml | 16 ++--
scripts/build-admina-framework-sdist.py | 92 +++++++++++++++++++
scripts/check-versions.py | 21 +----
tests/test_admina_alias.py | 56 -----------
tests/test_admina_framework_sdist.py | 84 +++++++++++++++++
tests/test_check_versions.py | 19 +---
tests/test_cli/test_init.py | 6 +-
tests/test_domains.py | 2 +-
tests/test_firewall_parity.py | 2 +-
.../test_loop_breaker_dependency.py | 2 +-
tests/test_lazy_imports.py | 2 +-
tests/test_plugins/test_scaffold_contracts.py | 2 +-
tests/test_presidio_engine.py | 2 +-
tests/test_redteam_external_corpora.py | 2 +-
tests/test_sdk/test_top_level_imports.py | 2 +-
tests/test_sdk/test_without_numpy.py | 4 +-
uv.lock | 20 ++--
53 files changed, 399 insertions(+), 329 deletions(-)
create mode 100644 packaging/admina-framework/README.md
create mode 100644 packaging/admina-framework/setup.py
delete mode 100644 packaging/admina/README.md
delete mode 100644 packaging/admina/pyproject.toml
create mode 100755 scripts/build-admina-framework-sdist.py
delete mode 100644 tests/test_admina_alias.py
create mode 100644 tests/test_admina_framework_sdist.py
diff --git a/.env.example b/.env.example
index dc4fd60..1cfa011 100644
--- a/.env.example
+++ b/.env.example
@@ -122,7 +122,7 @@ GRAFANA_ADMIN_PASSWORD=
# PII detection engine: spacy-regex (default) | presidio | the name of an
# engine another package registers in the admina.pii_engines entry-point group.
-# presidio requires: pip install "admina-framework[presidio]"
+# presidio requires: pip install "admina[presidio]"
# ADMINA_PII_ENGINE=spacy-regex
# PII masks: typed (default: [EMAIL], [PERSON], ...) | omissis ([OMISSIS] for
diff --git a/.github/workflows/release-core.yml b/.github/workflows/release-core.yml
index 809e576..7058669 100644
--- a/.github/workflows/release-core.yml
+++ b/.github/workflows/release-core.yml
@@ -29,7 +29,7 @@
# Workflow name: release-core.yml (this file)
# Environment name: pypi-core
#
-# The matching trusted publisher for admina-framework is configured for
+# The matching trusted publisher for admina is configured for
# release.yml (the other workflow).
#
# ── How to cut a release ───────────────────────────────────────────────────
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index a0f28c9..64e9cfd 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -14,10 +14,12 @@
# Release pipeline — fires on version tags (vX.Y.Z).
#
-# Publishes the pure-Python root package admina-framework to PyPI, then the
-# alias package admina (packaging/admina: no code, it installs
-# admina-framework at the same version), and creates the GitHub Release. The Rust accelerator admina-core is built
-# and published by a sibling workflow (.github/workflows/release-core.yml)
+# Publishes the pure-Python root package admina to PyPI and creates the
+# GitHub Release. On the v0.14.0 tag it also publishes the last
+# admina-framework release (packaging/admina-framework, built by
+# scripts/build-admina-framework-sdist.py): a source distribution that stops
+# its installation with the commands to switch to admina. The Rust
+# accelerator admina-core is built and published by a sibling workflow (.github/workflows/release-core.yml)
# triggered by the same tag — they run in parallel, independently.
#
# Why two workflows? PyPI Trusted Publishers had a transient issue
@@ -29,15 +31,15 @@
# Before the first run, configure the publisher on pypi.org:
#
# https://pypi.org/manage/account/publishing/ → Add pending publisher
-# PyPI Project Name: admina-framework
+# PyPI Project Name: admina
# Owner: admina-org
# Repository name: admina
# Workflow name: release.yml
# Environment name: pypi
#
-# The publisher of the alias project "admina" has the same values, with
-# PyPI Project Name: admina. The matching publisher for admina-core is
-# configured for release-core.yml.
+# The publisher of the project "admina-framework" has the same values, with
+# PyPI Project Name: admina-framework. The matching publisher for
+# admina-core is configured for release-core.yml.
# See https://docs.pypi.org/trusted-publishers/ for details.
#
# ── How to cut a release ───────────────────────────────────────────────────
@@ -105,9 +107,10 @@ jobs:
run: |
uv build --out-dir dist/
- - name: Build the admina alias (sdist + wheel)
+ - name: Build the last admina-framework release (sdist)
+ if: github.ref_name == 'v0.14.0'
run: |
- uv build packaging/admina --out-dir dist-alias/
+ python3 scripts/build-admina-framework-sdist.py dist-framework/
- name: Upload artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
@@ -117,17 +120,18 @@ jobs:
if-no-files-found: error
retention-days: 7
- - name: Upload alias artifact
+ - name: Upload admina-framework artifact
+ if: github.ref_name == 'v0.14.0'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
- name: admina-alias-dist
- path: dist-alias/*
+ name: admina-framework-dist
+ path: dist-framework/*
if-no-files-found: error
retention-days: 7
- # ── 4. Publish root admina-framework to PyPI ────────────────────────────
+ # ── 4. Publish root admina to PyPI ──────────────────────────────────────
publish-root:
- name: Publish admina-framework to PyPI
+ name: Publish admina to PyPI
needs: [build-root, tests]
# admina-core (the optional Rust accelerator) is published by the
# sibling workflow release-core.yml, in parallel with this one.
@@ -136,7 +140,7 @@ jobs:
runs-on: ubuntu-latest
environment:
name: pypi
- url: https://pypi.org/project/admina-framework/
+ url: https://pypi.org/project/admina/
steps:
- name: Download root artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
@@ -153,20 +157,20 @@ jobs:
packages-dir: dist/
skip-existing: true
- # ── 5. Publish the admina alias to PyPI ─────────────────────────────────
- publish-alias:
- name: Publish admina (alias) to PyPI
- # After admina-framework, so that the version the alias pins exists.
+ # ── 5. Publish the last admina-framework release (v0.14.0 only) ─────────
+ publish-framework-final:
+ name: Publish the last admina-framework release to PyPI
+ if: github.ref_name == 'v0.14.0'
needs: [build-root, tests, publish-root]
runs-on: ubuntu-latest
environment:
name: pypi
- url: https://pypi.org/project/admina/
+ url: https://pypi.org/project/admina-framework/
steps:
- - name: Download alias artifacts
+ - name: Download admina-framework artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
- name: admina-alias-dist
+ name: admina-framework-dist
path: dist
- name: List artifacts
@@ -188,7 +192,7 @@ jobs:
with:
fetch-depth: 0 # needed to extract the CHANGELOG section for this tag
- - name: Download admina-framework artifacts
+ - name: Download admina artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: admina-root-dist
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c2c557b..7fb8cd9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,13 +13,39 @@ stability commitment. See [ROADMAP.md](ROADMAP.md) for planned milestones.
## [Unreleased]
-### Notes
+## [0.14.0] — 2026-10-07
+
+Minor release with a declared breaking change: Admina is published on PyPI
+as `admina`. The module (`import admina`), the `admina` command, the extras,
+`admina-core` and the container images do not change. Upgrading an
+installation of `admina-framework`: see `docs/guides/package-name.md`.
+
+### Changed
+
+- **Breaking:** the distribution is named `admina` (`pip install admina`,
+ `pip install "admina[proxy]"`). `admina-framework` 0.14.0 is its last
+ release: a source distribution that stops the installation with the
+ commands to switch (`pip uninstall -y admina admina-framework && pip
+ install admina`) and leaves the installed packages as they are. A pin
+ such as `admina-framework<0.14` keeps installing the 0.13 releases.
+- The `admina` package holds the code; in 0.13.2 it was an alias that
+ installed `admina-framework`.
+- The `[rust]` extra requires `admina-core>=0.14.0,<0.15`.
+- The `pyproject.toml` that `admina plugin create` writes depends on
+ `admina>=`. A plugin created by an earlier release depends on
+ `admina-framework`: change that line (see the guide).
+- Install hints in error messages, warnings, the README and the docs name
+ `admina[...]`.
+- The Markdown compliance report of the dashboard ends with the forensic
+ section.
+
+### Documentation
-- From 0.14.0, Admina is published on PyPI as `admina`, and
- `admina-framework` is no longer released after a final 0.14.0 that stops
- the installation with the commands to switch. The module, the `admina`
- command and the extras do not change. `docs/guides/package-name.md`
- describes the upgrade.
+- `docs/guides/package-name.md`: the package names by release, the last
+ `admina-framework` release, the upgrade with pip, uv and dependency
+ lists, and the reinstall that restores the module when
+ `admina-framework` is uninstalled after `admina` was installed over it.
+- ROADMAP: the 0.14.0 entry and the released milestones.
## [0.13.2] — 2026-10-07
@@ -2240,7 +2266,8 @@ environment in `docker-compose.benchmark.yml`.
---
-[Unreleased]: https://github.com/admina-org/admina/compare/v0.13.2...HEAD
+[Unreleased]: https://github.com/admina-org/admina/compare/v0.14.0...HEAD
+[0.14.0]: https://github.com/admina-org/admina/compare/v0.13.2...v0.14.0
[0.13.2]: https://github.com/admina-org/admina/compare/v0.13.1...v0.13.2
[0.13.1]: https://github.com/admina-org/admina/compare/v0.13.0...v0.13.1
[0.13.0]: https://github.com/admina-org/admina/compare/v0.12.2...v0.13.0
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 5065235..0332f86 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -178,7 +178,7 @@ forwarded by the proxy.
**Import conventions**:
- Everything is nested under `admina.*` — there are no other top-level
- packages on `sys.path` after `pip install admina-framework`.
+ packages on `sys.path` after `pip install admina`.
- Inside the package, prefer absolute imports: `from admina.core.types import RiskLevel`
- Relative imports (`from .types import ...`) are accepted inside the same sub-package.
diff --git a/MODEL_CARD.md b/MODEL_CARD.md
index b5218fa..b0743a9 100644
--- a/MODEL_CARD.md
+++ b/MODEL_CARD.md
@@ -270,10 +270,10 @@ Detects and redacts PII in text. Three modes:
opt-in via `ADMINA_ENGINE=rust`. Categories are individually
toggleable from `admina.yaml`
(`admina/domains/data_sovereignty/pii.py:41-108`).
-- **Regex + spaCy NER** (`pip install admina-framework[nlp]`): adds named-entity
+- **Regex + spaCy NER** (`pip install admina[nlp]`): adds named-entity
detection for `PERSON`, `ORG`, `GPE`, `LOC`. Python only
(`admina/domains/data_sovereignty/pii.py:60-77`).
-- **Microsoft Presidio** (`pip install admina-framework[presidio]`,
+- **Microsoft Presidio** (`pip install admina[presidio]`,
selected with `ADMINA_PII_ENGINE=presidio` or `pii_engine: presidio`
in `admina.yaml`): a third, opt-in detection engine. Presidio does
**detection only** — Admina keeps its own masking, so the output
@@ -285,7 +285,7 @@ Detects and redacts PII in text. Three modes:
- **English-trained NER model.** The shipped `en_core_web_sm` is a
small English model. It under-detects names and organizations in
Italian, French, German, Spanish, etc. For multilingual deployments,
- switch to the Presidio engine (`admina-framework[presidio]` +
+ switch to the Presidio engine (`admina[presidio]` +
`ADMINA_PII_ENGINE=presidio`) and download the per-language spaCy
models it needs. Note that on Admina's own corpus Presidio measures
*lower* type-level recall than the default spaCy+regex engine on
diff --git a/README.md b/README.md
index 33a2868..c4a7663 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@
-
+
@@ -20,7 +20,7 @@
-
+
@@ -117,58 +117,58 @@ report = kit.gap_analysis(risk_category="high", current_compliance={...})
### Install from PyPI
-> **Package name change in 0.14.0.** From 0.14.0, Admina is published as
-> `admina`, and `admina-framework` is no longer released (a breaking change
-> for anything that installs `admina-framework`). The module, the `admina`
-> command and the extras do not change. See
-> [The `admina` package name](https://github.com/admina-org/admina/blob/main/docs/guides/package-name.md) for the steps to upgrade.
+> **Package name since 0.14.0.** Admina is published as `admina`; up to
+> 0.13.x it was `admina-framework`, which is no longer released. The module,
+> the `admina` command and the extras did not change. To upgrade an
+> installation of `admina-framework`, see
+> [The `admina` package name](https://github.com/admina-org/admina/blob/main/docs/guides/package-name.md).
```bash
# Recommended for new users: SDK + proxy + dashboard.
# Lets you run `admina dev` and see the dashboard out of the box.
-pip install "admina-framework[proxy]"
+pip install "admina[proxy]"
# Everything (proxy + NLP + telemetry). Use this if you also want
# spaCy-based NER for PII detection or OpenTelemetry export.
-pip install "admina-framework[full]"
+pip install "admina[full]"
python -m spacy download en_core_web_sm # for [full] only
# Model-adapter provider SDKs (per-provider extras):
-pip install "admina-framework[openai]" # openai>=1.0
-pip install "admina-framework[ollama]" # ollama>=0.3
-pip install "admina-framework[anthropic]" # anthropic>=0.39
-pip install "admina-framework[mistral]" # mistralai>=1.0
-pip install "admina-framework[gemini]" # google-genai>=1.0
-pip install "admina-framework[bedrock]" # boto3>=1.34 (AWS Bedrock)
+pip install "admina[openai]" # openai>=1.0
+pip install "admina[ollama]" # ollama>=0.3
+pip install "admina[anthropic]" # anthropic>=0.39
+pip install "admina[mistral]" # mistralai>=1.0
+pip install "admina[gemini]" # google-genai>=1.0
+pip install "admina[bedrock]" # boto3>=1.34 (AWS Bedrock)
# All provider SDKs at once:
-pip install "admina-framework[adapters]"
+pip install "admina[adapters]"
# Everything (proxy + NLP + telemetry + all adapters):
-pip install "admina-framework[all]"
+pip install "admina[all]"
python -m spacy download en_core_web_sm # for [all] only
# Optional: Rust-accelerated engine (auto-detected at runtime).
# Opt-in extra — pulls in the admina-core wheel from PyPI.
-pip install "admina-framework[rust]"
+pip install "admina[rust]"
# The proxy for the OpenAI-compatible gateway only: without Redis,
# ClickHouse, boto3 and the scientific stack (see "Embedded deployment").
-pip install "admina-framework[proxy-minimal]"
+pip install "admina[proxy-minimal]"
# Advanced: SDK only (no proxy, no dashboard, no `admina dev`).
# Use this when embedding the SDK into another service and you don't
# need the local dev server.
-pip install admina-framework
+pip install admina
```
-> The PyPI distribution name is `admina-framework`; the Python import
+> The PyPI distribution name is `admina`; the Python import
> name is `admina` (e.g. `from admina import GovernedModel`). This is
> a normal Python pattern — same as `python-dateutil` → `import dateutil`.
> The Rust engine is an **optional, opt-in** accelerator. The default
-> `pip install admina-framework` ships only the pure-Python implementation;
-> `admina-framework[rust]` adds the `admina-core` wheel, which Admina
+> `pip install admina` ships only the pure-Python implementation;
+> `admina[rust]` adds the `admina-core` wheel, which Admina
> auto-detects at runtime: under `ADMINA_ENGINE=auto` (the default) the
> firewall and the loop breaker run on Rust whenever it is installed, as in
> the official proxy image (see [Engine selection](#engine-selection)).
@@ -180,11 +180,6 @@ pip install admina-framework
> matters more than that extra coverage. See
> [Performance](#performance--hybrid-python--rust-engine) for the trade-off.
-Up to 0.13.x the PyPI package `admina` is an alias: `pip install admina`
-installs `admina-framework` at the same version, and `admina[X]` installs
-`admina-framework[X]` for every extra above. From 0.14.0 `admina` holds the
-code (see [The `admina` package name](https://github.com/admina-org/admina/blob/main/docs/guides/package-name.md)).
-
### Or install from source
```bash
@@ -278,11 +273,11 @@ from admina import GovernedModel, GovernedData, GovernedAgent, ComplianceKit
| Transport Adapter | MCP, HTTP REST |
| Forensic Store | Filesystem, S3-compatible (boto3 — AWS S3, MinIO, R2, …) |
| Auth Provider | API Key |
-| PII Engine | spaCy + Regex (default), Microsoft Presidio (`pip install admina-framework[presidio]`, `ADMINA_PII_ENGINE=presidio`); engines of other packages through the `admina.pii_engines` entry-point group (see [PII engines](#pii-engines)) |
+| PII Engine | spaCy + Regex (default), Microsoft Presidio (`pip install admina[presidio]`, `ADMINA_PII_ENGINE=presidio`); engines of other packages through the `admina.pii_engines` entry-point group (see [PII engines](#pii-engines)) |
| Alert Channel | Log, Webhook |
Model adapters lazy-import their provider SDK, so install the ones you
-use: `pip install admina-framework[adapters]` for all of them, or a
+use: `pip install admina[adapters]` for all of them, or a
single provider (`[openai]`, `[ollama]`, `[anthropic]`, `[mistral]`,
`[gemini]`, `[bedrock]`). vLLM has no extra of its own — it serves an
OpenAI-compatible API and subclasses the OpenAI adapter, so install
@@ -478,7 +473,7 @@ that only the Python firewall applies: `agent_security.firewall`
`pattern_packs` (an empty list is not set). The error names the cause:
```
-ADMINA_ENGINE=rust, but admina-core is not installed: install admina-framework[rust], or set ADMINA_ENGINE=python (or auto) to run the Python engines
+ADMINA_ENGINE=rust, but admina-core is not installed: install admina[rust], or set ADMINA_ENGINE=python (or auto) to run the Python engines
ADMINA_ENGINE=rust, but admina.yaml sets agent_security.firewall.pattern_packs, which only the Python firewall applies: remove them, or set ADMINA_ENGINE=python (or auto) to run the Python firewall
```
@@ -982,7 +977,7 @@ attempt. The `proxy-minimal` extra installs the proxy without Redis,
ClickHouse, boto3, typer and the scientific stack of the Python loop breaker:
```bash
-pip install "admina-framework[proxy-minimal]"
+pip install "admina[proxy-minimal]"
ADMINA_ENABLED_SURFACES=gateway REDIS_URL= CLICKHOUSE_HOST= \
ADMINA_API_KEY_FILE=/run/secrets/admina_api_key \
uvicorn admina.proxy.main:app --host 0.0.0.0 --port 8080
@@ -1676,7 +1671,7 @@ cd integrations/cheshirecat/admina-plugin
Governs every LLM call and tool invocation in-process. Install
-`admina-framework[proxy,nlp]`: the loop detection that the callbacks turn
+`admina[proxy,nlp]`: the loop detection that the callbacks turn
on by default needs scikit-learn from `[proxy]` (or pass
`loop_detection=False`).
@@ -1708,9 +1703,9 @@ See [full integration docs](https://github.com/admina-org/admina/blob/main/docs/
## Performance — Hybrid Python + Rust engine
The Rust core engine is an optional accelerator. The default
-`pip install admina-framework` ships only the pure-Python implementation;
+`pip install admina` ships only the pure-Python implementation;
enable the Rust engine with the opt-in extra `pip install
-"admina-framework[rust]"` (or build from source for local development —
+"admina[rust]"` (or build from source for local development —
`maturin develop --release --manifest-path core-rust/Cargo.toml`, see
[CONTRIBUTING.md](https://github.com/admina-org/admina/blob/main/CONTRIBUTING.md)). Under `ADMINA_ENGINE=auto` Admina
runs the Rust firewall and loop breaker when the extension is installed and
diff --git a/ROADMAP.md b/ROADMAP.md
index ff8025e..956e733 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -6,7 +6,7 @@ not on a calendar. Scope may shift in response to user feedback, security
findings, or upstream changes in the governance landscape (EU AI Act
implementing acts, new frameworks, new attack classes).
-The current release is **0.13.2**. Admina is pre-1.0: the public API may
+The current release is **0.14.0**. Admina is pre-1.0: the public API may
still evolve before the 1.0 stability commitment, so a minor release may
carry a declared breaking change once its replacement is in place. Shipped
detail lives in [CHANGELOG.md](CHANGELOG.md).
diff --git a/SECURITY.md b/SECURITY.md
index 9e83c56..2a92bd5 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -4,8 +4,8 @@
| Version | Supported |
|---------|-----------|
-| 0.13.x | Yes |
-| < 0.13 | No |
+| 0.14.x | Yes |
+| < 0.14 | No |
During the pre-1.0 phase only the latest minor line receives security
fixes. Once 1.0 ships, an LTS window will be defined in
diff --git a/admina/__init__.py b/admina/__init__.py
index ab5a834..a9e5ab0 100644
--- a/admina/__init__.py
+++ b/admina/__init__.py
@@ -23,7 +23,7 @@
from admina.sdk import ComplianceKit, GovernedAgent, GovernedData, GovernedModel
-__version__ = "0.13.2"
+__version__ = "0.14.0"
__all__ = [
"__version__",
diff --git a/admina/cli/main.py b/admina/cli/main.py
index 3d0588a..4aef347 100644
--- a/admina/cli/main.py
+++ b/admina/cli/main.py
@@ -300,7 +300,7 @@ def _format_next_steps(project_name: str) -> str:
[
"",
" To run the local proxy + dashboard (admina dev), install the [proxy] extra:",
- " pip install 'admina-framework[proxy]' --upgrade",
+ " pip install 'admina[proxy]' --upgrade",
]
)
if docker_ok:
@@ -555,11 +555,11 @@ def _require_proxy_extra_for_local_dev() -> None:
click.echo(" admina dev (local mode) requires the [proxy] extra.", err=True)
click.echo(" Install one of:", err=True)
click.echo(
- " pip install 'admina-framework[proxy]' --upgrade",
+ " pip install 'admina[proxy]' --upgrade",
err=True,
)
click.echo(
- " pip install 'admina-framework[full]' --upgrade # adds NLP + telemetry",
+ " pip install 'admina[full]' --upgrade # adds NLP + telemetry",
err=True,
)
click.echo("", err=True)
@@ -1226,11 +1226,11 @@ def doctor() -> None:
if extras_status.get("proxy") != "ok":
click.echo(
f" {warn_mark} admina dev (local mode) needs the [proxy] extra — "
- "pip install 'admina-framework[proxy]' --upgrade"
+ "pip install 'admina[proxy]' --upgrade"
)
issues.append(
"admina dev (local mode) requires the [proxy] extra — "
- "run: pip install 'admina-framework[proxy]' --upgrade"
+ "run: pip install 'admina[proxy]' --upgrade"
)
# ── spaCy NER model ──────────────────────────────────────
diff --git a/admina/cli/templates/plugin_pyproject.toml.j2 b/admina/cli/templates/plugin_pyproject.toml.j2
index efa238e..fa239c0 100644
--- a/admina/cli/templates/plugin_pyproject.toml.j2
+++ b/admina/cli/templates/plugin_pyproject.toml.j2
@@ -8,10 +8,10 @@ version = "0.1.0"
description = "Admina {{ plugin_type.replace('_', ' ') }} plugin: {{ plugin_name }}"
requires-python = ">=3.11"
dependencies = [
- # Floor pinned to the admina-framework version that generated this
+ # Floor pinned to the admina version that generated this
# scaffold: the plugin contract (class-attr name, async ABC methods)
# requires at least that runtime.
- "admina-framework>={{ admina_version }}",
+ "admina>={{ admina_version }}",
]
[project.entry-points."admina.plugins"]
diff --git a/admina/domains/data_sovereignty/pii.py b/admina/domains/data_sovereignty/pii.py
index 688ff2a..1629245 100644
--- a/admina/domains/data_sovereignty/pii.py
+++ b/admina/domains/data_sovereignty/pii.py
@@ -208,7 +208,7 @@ def __init__(self, config=None, *, mask_style: str = "typed"):
if _spacy is None:
logger.info(
"spaCy not installed — PII redaction running in regex-only mode "
- "(install admina-framework[nlp] for NER-based detection)"
+ "(install admina[nlp] for NER-based detection)"
)
self.nlp = None
else:
diff --git a/admina/engines/__init__.py b/admina/engines/__init__.py
index 989da75..7558aac 100644
--- a/admina/engines/__init__.py
+++ b/admina/engines/__init__.py
@@ -129,7 +129,7 @@ def _require_rust() -> None:
if not _rust_available:
raise EngineSelectionError(
"ADMINA_ENGINE=rust, but admina-core is not installed: install "
- "admina-framework[rust], or set ADMINA_ENGINE=python (or auto) to run "
+ "admina[rust], or set ADMINA_ENGINE=python (or auto) to run "
"the Python engines"
)
@@ -458,7 +458,7 @@ def __init__(self, **kwargs):
except ImportError as exc:
raise ImportError(
"The Python loop breaker needs numpy and scikit-learn: "
- "pip install 'admina-framework[proxy]' (or 'admina-framework[rust]' "
+ "pip install 'admina[proxy]' (or 'admina[rust]' "
"for the Rust loop breaker)"
) from exc
diff --git a/admina/engines/presidio.py b/admina/engines/presidio.py
index df53f20..60aa591 100644
--- a/admina/engines/presidio.py
+++ b/admina/engines/presidio.py
@@ -36,7 +36,7 @@
E-mail domains are checked against the public suffix list bundled with
tldextract, with no cache and no download, so the engine makes no network
access. Presidio is an optional extra:
- pip install 'admina-framework[presidio]'
+ pip install 'admina[presidio]'
"""
from __future__ import annotations
@@ -240,7 +240,7 @@ def __init__(
except ImportError as exc:
raise ImportError(
"The Presidio PII engine requires the [presidio] extra. Install it with "
- "`pip install 'admina-framework[presidio]'`."
+ "`pip install 'admina[presidio]'`."
) from exc
pipelines = _pipelines(nlp_models)
diff --git a/admina/integrations/_engines.py b/admina/integrations/_engines.py
index e3dd563..b84d059 100644
--- a/admina/integrations/_engines.py
+++ b/admina/integrations/_engines.py
@@ -74,8 +74,8 @@ def get_loop_breaker():
raise ImportError(
"Loop detection (loop_detection=True, the default of the "
"LangChain and CrewAI callbacks) needs numpy and scikit-learn: "
- "pip install 'admina-framework[proxy]' (or "
- "'admina-framework[rust]' for the Rust loop breaker), or pass "
+ "pip install 'admina[proxy]' (or "
+ "'admina[rust]' for the Rust loop breaker), or pass "
"loop_detection=False"
) from exc
return _loop_breaker
diff --git a/admina/integrations/crewai/README.md b/admina/integrations/crewai/README.md
index 3b1c0a1..8b9ca94 100644
--- a/admina/integrations/crewai/README.md
+++ b/admina/integrations/crewai/README.md
@@ -9,7 +9,7 @@ pip install -e ".[proxy,nlp]" # Admina: loop breaker (numpy, scikit-learn) + N
pip install crewai # Your CrewAI deps
```
-From PyPI: `pip install "admina-framework[proxy,nlp]"`. Loop detection is on
+From PyPI: `pip install "admina[proxy,nlp]"`. Loop detection is on
by default and needs numpy and scikit-learn (`[proxy]`), or the Rust loop
breaker (`[rust]`); without either, pass `loop_detection=False`.
diff --git a/admina/integrations/langchain/README.md b/admina/integrations/langchain/README.md
index dd620aa..8911741 100644
--- a/admina/integrations/langchain/README.md
+++ b/admina/integrations/langchain/README.md
@@ -9,7 +9,7 @@ pip install -e ".[proxy,nlp]" # Admina: loop breaker (numpy, scikit-learn) + N
pip install langchain # Your LangChain deps
```
-From PyPI: `pip install "admina-framework[proxy,nlp]"`. Loop detection is on
+From PyPI: `pip install "admina[proxy,nlp]"`. Loop detection is on
by default and needs numpy and scikit-learn (`[proxy]`), or the Rust loop
breaker (`[rust]`); without either, pass `loop_detection=False`.
diff --git a/admina/plugins/builtin/adapters/anthropic.py b/admina/plugins/builtin/adapters/anthropic.py
index 7e26a39..7c51c6d 100644
--- a/admina/plugins/builtin/adapters/anthropic.py
+++ b/admina/plugins/builtin/adapters/anthropic.py
@@ -17,7 +17,7 @@
Wraps the ``anthropic`` Python client (sync) to provide inference through
the Anthropic Messages API.
-Requires: ``pip install 'admina-framework[anthropic]'`` (optional dependency).
+Requires: ``pip install 'admina[anthropic]'`` (optional dependency).
"""
from __future__ import annotations
@@ -74,7 +74,7 @@ def _get_client(self) -> Any:
except ImportError as exc:
raise ImportError(
"The 'anthropic' package is required for AnthropicAdapter. "
- "Install it with: pip install 'admina-framework[anthropic]'"
+ "Install it with: pip install 'admina[anthropic]'"
) from exc
kwargs: dict[str, Any] = {}
if self._api_key:
diff --git a/admina/plugins/builtin/adapters/bedrock.py b/admina/plugins/builtin/adapters/bedrock.py
index c1606a0..97a300d 100644
--- a/admina/plugins/builtin/adapters/bedrock.py
+++ b/admina/plugins/builtin/adapters/bedrock.py
@@ -18,7 +18,7 @@
Converse API. Authentication follows the standard AWS credential chain
(environment variables, ``~/.aws/credentials``, IAM instance role, etc.).
-Requires: ``pip install 'admina-framework[bedrock]'`` (optional dependency).
+Requires: ``pip install 'admina[bedrock]'`` (optional dependency).
"""
from __future__ import annotations
@@ -80,7 +80,7 @@ def _get_client(self) -> Any:
except ImportError as exc:
raise ImportError(
"The 'boto3' package is required for BedrockAdapter. "
- "Install it with: pip install 'admina-framework[bedrock]'"
+ "Install it with: pip install 'admina[bedrock]'"
) from exc
self._client = boto3.client("bedrock-runtime", region_name=self._region)
return self._client
diff --git a/admina/plugins/builtin/adapters/gemini.py b/admina/plugins/builtin/adapters/gemini.py
index 3cd0df9..5e80a4d 100644
--- a/admina/plugins/builtin/adapters/gemini.py
+++ b/admina/plugins/builtin/adapters/gemini.py
@@ -17,7 +17,7 @@
Wraps the ``google-genai`` Python client (sync) to provide inference through
the Google Gemini GenerateContent API.
-Requires: ``pip install 'admina-framework[gemini]'`` (optional dependency).
+Requires: ``pip install 'admina[gemini]'`` (optional dependency).
"""
from __future__ import annotations
@@ -70,7 +70,7 @@ def _get_client(self) -> Any:
except ImportError as exc:
raise ImportError(
"The 'google-genai' package is required for GeminiAdapter. "
- "Install it with: pip install 'admina-framework[gemini]'"
+ "Install it with: pip install 'admina[gemini]'"
) from exc
kwargs: dict[str, Any] = {}
if self._api_key:
diff --git a/admina/plugins/builtin/adapters/mistral.py b/admina/plugins/builtin/adapters/mistral.py
index aa570e1..de982aa 100644
--- a/admina/plugins/builtin/adapters/mistral.py
+++ b/admina/plugins/builtin/adapters/mistral.py
@@ -17,7 +17,7 @@
Wraps the ``mistralai`` Python client (sync) to provide inference through
the Mistral Chat Completions API.
-Requires: ``pip install 'admina-framework[mistral]'`` (optional dependency).
+Requires: ``pip install 'admina[mistral]'`` (optional dependency).
"""
from __future__ import annotations
@@ -70,7 +70,7 @@ def _get_client(self) -> Any:
except ImportError as exc:
raise ImportError(
"The 'mistralai' package is required for MistralAdapter. "
- "Install it with: pip install 'admina-framework[mistral]'"
+ "Install it with: pip install 'admina[mistral]'"
) from exc
kwargs: dict[str, Any] = {}
if self._api_key:
diff --git a/admina/proxy/main.py b/admina/proxy/main.py
index c9787f5..d5182cf 100644
--- a/admina/proxy/main.py
+++ b/admina/proxy/main.py
@@ -485,7 +485,7 @@ def _build_loop_breaker() -> Any:
raise RuntimeError(
"The mcp and integration surfaces need the loop breaker, whose Python "
f"engine needs numpy and scikit-learn ({exc}). Install "
- "admina-framework[proxy] (or admina-framework[rust]), or serve only the "
+ "admina[proxy] (or admina[rust]), or serve only the "
"gateway: ADMINA_ENABLED_SURFACES=gateway."
) from exc
@@ -548,7 +548,7 @@ async def _connect_redis(url: str) -> Any:
except ImportError:
logger.warning(
"REDIS_URL is set but the redis package is not installed "
- "(pip install 'admina-framework[proxy]') — continuing without rate-limit cache"
+ "(pip install 'admina[proxy]') — continuing without rate-limit cache"
)
return None
try:
@@ -573,7 +573,7 @@ def _connect_clickhouse() -> Any:
except ImportError:
logger.warning(
"CLICKHOUSE_HOST is set but clickhouse-connect is not installed "
- "(pip install 'admina-framework[proxy]') — analytics disabled"
+ "(pip install 'admina[proxy]') — analytics disabled"
)
return None
try:
@@ -1659,12 +1659,6 @@ def _emit(section: str, obj: Any, prefix: str = "") -> None:
lines.append(f"- Chain head: `{(fb.get('chain_head') or 'GENESIS')[:16]}...`")
else:
lines.append("- Forensic backend not configured")
- lines += [
- "",
- "---",
- "*OSS-tier report. PDF / Excel / branded reporting are not "
- "included in admina-framework.*",
- ]
return Response(
content="\n".join(lines) + "\n",
media_type="text/markdown; charset=utf-8",
diff --git a/admina/redteam/__init__.py b/admina/redteam/__init__.py
index f96ea98..267ca60 100644
--- a/admina/redteam/__init__.py
+++ b/admina/redteam/__init__.py
@@ -119,7 +119,7 @@ def _no_engine_reason(adapter: Any, engines: list[str], config: str | Path | Non
"""Why none of *engines* can run the corpora of *adapter*."""
if "rust" in engines:
if not detectors.rust_available():
- return "admina-core is not installed (install admina-framework[rust])"
+ return "admina-core is not installed (install admina[rust])"
keys = adapter.python_only_keys() if hasattr(adapter, "python_only_keys") else []
if keys:
names = ", ".join(f"agent_security.firewall.{key}" for key in keys)
diff --git a/core-rust/Cargo.lock b/core-rust/Cargo.lock
index 8d68a72..3ebecfe 100644
--- a/core-rust/Cargo.lock
+++ b/core-rust/Cargo.lock
@@ -4,7 +4,7 @@ version = 4
[[package]]
name = "admina_core"
-version = "0.13.2"
+version = "0.14.0"
dependencies = [
"chrono",
"hex",
diff --git a/core-rust/Cargo.toml b/core-rust/Cargo.toml
index e626d2f..17ec386 100644
--- a/core-rust/Cargo.toml
+++ b/core-rust/Cargo.toml
@@ -15,7 +15,7 @@
[package]
name = "admina_core"
-version = "0.13.2"
+version = "0.14.0"
edition = "2021"
description = "Admina governance engines — Rust core with Python bindings"
license = "Apache-2.0"
diff --git a/core-rust/README.md b/core-rust/README.md
index 4e82aa2..c1987ba 100644
--- a/core-rust/README.md
+++ b/core-rust/README.md
@@ -17,7 +17,7 @@ pip install admina-core
The Python framework auto-detects the Rust engine at runtime and falls
back to a pure-Python implementation if `admina-core` is not installed,
-so `admina-framework` always works on its own.
+so `admina` always works on its own.
## License
diff --git a/core-rust/pyproject.toml b/core-rust/pyproject.toml
index 43d2f1b..bd984ff 100644
--- a/core-rust/pyproject.toml
+++ b/core-rust/pyproject.toml
@@ -19,7 +19,7 @@ build-backend = "maturin"
[project]
name = "admina-core"
-version = "0.13.2"
+version = "0.14.0"
description = "Admina governance engines — Rust core with Python bindings (firewall, PII, loop breaker, hash chain)"
readme = "README.md"
requires-python = ">=3.11"
diff --git a/core-rust/uv.lock b/core-rust/uv.lock
index 82123dc..9a206d9 100644
--- a/core-rust/uv.lock
+++ b/core-rust/uv.lock
@@ -4,5 +4,5 @@ requires-python = ">=3.11"
[[package]]
name = "admina-core"
-version = "0.13.2"
+version = "0.14.0"
source = { editable = "." }
diff --git a/docs/guides/airgapped-deployment.md b/docs/guides/airgapped-deployment.md
index 0d39e06..c82dfcb 100644
--- a/docs/guides/airgapped-deployment.md
+++ b/docs/guides/airgapped-deployment.md
@@ -63,7 +63,7 @@ If you operate a private registry, `docker tag` + `docker push` to
### 2. Python wheels
-`pip install admina-framework[full]` resolves ~150 wheels from PyPI.
+`pip install admina[full]` resolves ~150 wheels from PyPI.
Pre-download them once on a connected host, then point pip at the
local directory.
@@ -71,13 +71,13 @@ local directory.
# On a host with Internet access:
mkdir -p admina-wheels
uv pip download \
- admina-framework[full] \
+ admina[full] \
--dest admina-wheels \
--python-version 3.11 \
--platform-extra manylinux2014_x86_64
# Transfer admina-wheels/ to the air-gapped host, then:
-uv pip install --no-index --find-links ./admina-wheels admina-framework[full]
+uv pip install --no-index --find-links ./admina-wheels admina[full]
```
For full reproducibility, pin the lock file: `uv lock --frozen`
diff --git a/docs/guides/package-name.md b/docs/guides/package-name.md
index 1e2319c..5ad5557 100644
--- a/docs/guides/package-name.md
+++ b/docs/guides/package-name.md
@@ -22,8 +22,10 @@ installation with this message, and leaves the installed packages as they
are:
```text
-admina-framework is now published as admina.
+admina-framework is now published as admina (since 0.14.0).
Run: pip uninstall -y admina admina-framework && pip install admina
+Extras keep their names: admina-framework[proxy] is admina[proxy].
+See https://github.com/admina-org/admina/blob/main/docs/guides/package-name.md
```
So `pip install -U admina-framework`, or a requirement `admina-framework`
diff --git a/docs/index.md b/docs/index.md
index 76be9f1..75bfcd2 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -32,13 +32,14 @@ audited, EU AI Act compliance tracked. Works in-process (SDK) and over the netwo
## Install
```bash
-pip install "admina-framework[proxy]" # Recommended: SDK + proxy + dashboard
-pip install "admina-framework[full]" # + NLP (spaCy) + telemetry (OTEL)
-pip install admina-framework # SDK only — embed it in another service
+pip install "admina[proxy]" # Recommended: SDK + proxy + dashboard
+pip install "admina[full]" # + NLP (spaCy) + telemetry (OTEL)
+pip install admina # SDK only — embed it in another service
```
-> Distribution name `admina-framework`, import name `admina`
-> (same pattern as `python-dateutil` → `import dateutil`).
+> Distribution name and import name: `admina` (up to 0.13.x the
+> distribution was `admina-framework`; see the
+> [package name guide](guides/package-name.md)).
## 30-second example
diff --git a/packaging/admina-framework/README.md b/packaging/admina-framework/README.md
new file mode 100644
index 0000000..6b5e68c
--- /dev/null
+++ b/packaging/admina-framework/README.md
@@ -0,0 +1,16 @@
+# admina-framework
+
+Admina is published as [`admina`](https://pypi.org/project/admina/) since
+0.14.0. This last release of `admina-framework` cannot be installed: it
+stops with the commands to switch.
+
+```bash
+pip uninstall -y admina admina-framework
+pip install admina # extras keep their names: "admina[proxy]"
+```
+
+The module (`import admina`), the `admina` command and the extras did not
+change. Releases up to 0.13.x of `admina-framework` remain installable with a
+pin such as `admina-framework<0.14`.
+
+Upgrade guide: https://github.com/admina-org/admina/blob/main/docs/guides/package-name.md
diff --git a/packaging/admina-framework/setup.py b/packaging/admina-framework/setup.py
new file mode 100644
index 0000000..369ed0b
--- /dev/null
+++ b/packaging/admina-framework/setup.py
@@ -0,0 +1,23 @@
+# Copyright © 2025–2026 Stefano Noferi & Admina contributors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+
+# The last release of admina-framework: the code is published as admina
+# since 0.14.0. Installing this source distribution stops with the commands
+# to switch, before pip or uv change the installed packages.
+
+raise SystemExit(
+ "\n\nadmina-framework is now published as admina (since 0.14.0).\n"
+ "Run: pip uninstall -y admina admina-framework && pip install admina\n"
+ "Extras keep their names: admina-framework[proxy] is admina[proxy].\n"
+ "See https://github.com/admina-org/admina/blob/main/docs/guides/package-name.md\n"
+)
diff --git a/packaging/admina/README.md b/packaging/admina/README.md
deleted file mode 100644
index 9b8fed1..0000000
--- a/packaging/admina/README.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# admina
-
-`pip install admina` installs [`admina-framework`](https://pypi.org/project/admina-framework/),
-the Admina governed AI development framework, at the same version. This
-package has no code of its own; `import admina` comes from `admina-framework`.
-
-Every extra of `admina-framework` is available under the same name:
-
-```bash
-pip install admina # SDK
-pip install "admina[proxy]" # SDK + proxy + dashboard
-pip install "admina[full]" # proxy + NLP + telemetry
-```
-
-Documentation, changelog and source: https://github.com/admina-org/admina
diff --git a/packaging/admina/pyproject.toml b/packaging/admina/pyproject.toml
deleted file mode 100644
index 5c8544c..0000000
--- a/packaging/admina/pyproject.toml
+++ /dev/null
@@ -1,69 +0,0 @@
-
-# Copyright © 2025–2026 Stefano Noferi & Admina contributors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# The PyPI project "admina": no code of its own, it installs admina-framework
-# at the same version. Every extra of admina-framework has an extra of the same
-# name here (admina[proxy] installs admina-framework[proxy]). The version and
-# the pin are checked by scripts/check-versions.py, the extras by
-# tests/test_admina_alias.py; release.yml publishes it after admina-framework.
-
-[build-system]
-requires = ["setuptools>=68.0"]
-build-backend = "setuptools.build_meta"
-
-[project]
-name = "admina"
-version = "0.13.2"
-description = "Admina — governed AI development framework (installs admina-framework)"
-readme = "README.md"
-requires-python = ">=3.11"
-license = "Apache-2.0"
-authors = [
- {name = "Stefano Noferi", email = "info@admina.org"},
-]
-maintainers = [
- {name = "Stefano Noferi", email = "info@admina.org"},
-]
-classifiers = [
- "Development Status :: 4 - Beta",
- "Programming Language :: Python :: 3",
-]
-dependencies = ["admina-framework==0.13.2"]
-
-[project.urls]
-Homepage = "https://admina.org"
-Repository = "https://github.com/admina-org/admina"
-Documentation = "https://github.com/admina-org/admina#readme"
-Changelog = "https://github.com/admina-org/admina/blob/main/CHANGELOG.md"
-
-[project.optional-dependencies]
-proxy = ["admina-framework[proxy]==0.13.2"]
-proxy-minimal = ["admina-framework[proxy-minimal]==0.13.2"]
-nlp = ["admina-framework[nlp]==0.13.2"]
-telemetry = ["admina-framework[telemetry]==0.13.2"]
-rust = ["admina-framework[rust]==0.13.2"]
-full = ["admina-framework[full]==0.13.2"]
-openai = ["admina-framework[openai]==0.13.2"]
-ollama = ["admina-framework[ollama]==0.13.2"]
-anthropic = ["admina-framework[anthropic]==0.13.2"]
-mistral = ["admina-framework[mistral]==0.13.2"]
-gemini = ["admina-framework[gemini]==0.13.2"]
-bedrock = ["admina-framework[bedrock]==0.13.2"]
-adapters = ["admina-framework[adapters]==0.13.2"]
-presidio = ["admina-framework[presidio]==0.13.2"]
-all = ["admina-framework[all]==0.13.2"]
-
-[tool.setuptools]
-packages = []
diff --git a/pyproject.toml b/pyproject.toml
index 0c5ae60..d6d8864 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -18,8 +18,8 @@ requires = ["setuptools>=68.0"]
build-backend = "setuptools.build_meta"
[project]
-name = "admina-framework"
-version = "0.13.2"
+name = "admina"
+version = "0.14.0"
description = "Admina — governed AI development framework"
readme = "README.md"
requires-python = ">=3.11"
@@ -101,7 +101,7 @@ nlp = [
# NOTE: the `en_core_web_sm` model is NOT declared as a direct
# dependency here because PyPI rejects wheels with URL-pinned
# dependencies (PEP 440 "direct references"). After installing
- # `admina-framework[nlp]`, run:
+ # `admina[nlp]`, run:
# python -m spacy download en_core_web_sm
# The PIIRedactor logs a clear error if the model is missing.
]
@@ -126,14 +126,14 @@ rust = [
# stops). It is opt-in so the default install keeps the broader Python
# detection coverage; enable [rust] when latency matters more.
#
- # admina-core and admina-framework are released in lockstep from the same
+ # admina-core and admina are released in lockstep from the same
# tag (scripts/check-versions.py enforces manifest alignment). The
# published metadata carries the version range below; uv.lock resolves
# admina-core from ./core-rust instead ([tool.uv.sources]), so
# `uv sync --extra rust` (or --all-extras) builds the engine of the same
# checkout, which needs a Rust toolchain. The range takes the 0.13.x
# releases of admina-core, the line released with this framework.
- "admina-core>=0.13.0,<0.14",
+ "admina-core>=0.14.0,<0.15",
]
# NOTE: the [guardrailsai] extra was removed in v0.9.0 because the
# upstream `guardrails-ai` package is currently in PyPI quarantine
@@ -141,7 +141,7 @@ rust = [
# admina/plugins/builtin/guards/guardrailsai_guard.py is preserved; if
# you have a local copy of guardrails-ai installed, the plugin will
# detect it and work. Re-introduce the extra once upstream is restored.
-full = ["admina-framework[proxy,nlp,telemetry]"]
+full = ["admina[proxy,nlp,telemetry]"]
# Model-adapter provider SDKs — each adapter lazy-imports its SDK, so the
# extra only controls whether the provider wheel is present.
openai = ["openai>=1.0,<4"]
@@ -150,7 +150,7 @@ anthropic = ["anthropic>=0.39,<2"]
mistral = ["mistralai>=1.0,<4"]
gemini = ["google-genai>=1.0,<3"]
bedrock = ["boto3>=1.34,<2"]
-adapters = ["admina-framework[openai,ollama,anthropic,mistral,gemini,bedrock]"]
+adapters = ["admina[openai,ollama,anthropic,mistral,gemini,bedrock]"]
# Microsoft Presidio as a selectable PII engine. Presidio does detection only;
# Admina keeps its own masking, so the output format matches the default
# spaCy+regex engine. ADMINA_PRESIDIO_NLP_MODELS sets the spaCy pipeline of each
@@ -159,7 +159,7 @@ adapters = ["admina-framework[openai,ollama,anthropic,mistral,gemini,bedrock]"]
# installed separately, like [nlp]:
# python -m spacy download en_core_web_sm
presidio = ["presidio-analyzer>=2.2,<3"]
-all = ["admina-framework[proxy,nlp,telemetry,adapters,presidio]"]
+all = ["admina[proxy,nlp,telemetry,adapters,presidio]"]
[dependency-groups]
dev = [
diff --git a/scripts/build-admina-framework-sdist.py b/scripts/build-admina-framework-sdist.py
new file mode 100755
index 0000000..db0c717
--- /dev/null
+++ b/scripts/build-admina-framework-sdist.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python3
+
+# Copyright © 2025–2026 Stefano Noferi & Admina contributors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Build the last admina-framework release: a source distribution that
+stops its installation with the commands to switch to ``admina``.
+
+Usage (from the repository root)::
+
+ python scripts/build-admina-framework-sdist.py dist-framework/
+
+The version is the one of ``pyproject.toml``. The archive holds
+``PKG-INFO`` (with ``packaging/admina-framework/README.md`` as the
+description), a ``pyproject.toml`` that names the setuptools backend, and
+``packaging/admina-framework/setup.py``, which raises ``SystemExit`` with the
+message: pip and uv run it before they change the installed packages.
+"""
+
+from __future__ import annotations
+
+import io
+import sys
+import tarfile
+import tomllib
+from pathlib import Path
+
+REPO = Path(__file__).resolve().parent.parent
+
+_PYPROJECT = """[build-system]
+requires = ["setuptools>=68.0"]
+build-backend = "setuptools.build_meta"
+"""
+
+
+def pkg_info(version: str, description: str) -> str:
+ """The core metadata of the release."""
+ return (
+ "Metadata-Version: 2.4\n"
+ "Name: admina-framework\n"
+ f"Version: {version}\n"
+ "Summary: Admina is published as admina: this release cannot be installed\n"
+ "Project-URL: Homepage, https://pypi.org/project/admina/\n"
+ "Project-URL: Repository, https://github.com/admina-org/admina\n"
+ "License-Expression: Apache-2.0\n"
+ "Requires-Python: >=3.11\n"
+ "Description-Content-Type: text/markdown\n"
+ "\n"
+ f"{description}"
+ )
+
+
+def build(out_dir: Path, repo: Path = REPO) -> Path:
+ """Write ``admina_framework-.tar.gz`` into *out_dir*."""
+ version = tomllib.loads((repo / "pyproject.toml").read_text(encoding="utf-8"))["project"][
+ "version"
+ ]
+ sources = repo / "packaging" / "admina-framework"
+ top = f"admina_framework-{version}"
+ files = {
+ "PKG-INFO": pkg_info(version, (sources / "README.md").read_text(encoding="utf-8")),
+ "pyproject.toml": _PYPROJECT,
+ "setup.py": (sources / "setup.py").read_text(encoding="utf-8"),
+ "README.md": (sources / "README.md").read_text(encoding="utf-8"),
+ }
+ out_dir.mkdir(parents=True, exist_ok=True)
+ target = out_dir / f"{top}.tar.gz"
+ with tarfile.open(target, "w:gz", format=tarfile.PAX_FORMAT) as archive:
+ for name, text in files.items():
+ data = text.encode("utf-8")
+ info = tarfile.TarInfo(f"{top}/{name}")
+ info.size = len(data)
+ info.mode = 0o644
+ archive.addfile(info, io.BytesIO(data))
+ return target
+
+
+if __name__ == "__main__":
+ if len(sys.argv) != 2:
+ sys.exit("usage: build-admina-framework-sdist.py OUT_DIR")
+ print(build(Path(sys.argv[1])))
diff --git a/scripts/check-versions.py b/scripts/check-versions.py
index ae96c14..4b4e48e 100755
--- a/scripts/check-versions.py
+++ b/scripts/check-versions.py
@@ -25,16 +25,14 @@
(see ``normalise()``). Any other difference is drift.
Tracked points of truth:
- 1. pyproject.toml → admina-framework (canonical)
+ 1. pyproject.toml → admina (canonical)
2. admina/__init__.py → runtime __version__
3. core-rust/pyproject.toml → admina-core (PyPI)
4. core-rust/Cargo.toml → admina_core crate
5. core-rust/Cargo.lock → resolved admina_core entry
- 6. uv.lock → resolved admina-framework entry
+ 6. uv.lock → resolved admina entry
7. uv.lock → resolved admina-core entry ([rust] extra)
8. core-rust/uv.lock → resolved admina-core entry
- 9. packaging/admina/pyproject.toml → admina (PyPI alias)
- 10. packaging/admina/pyproject.toml → its admina-framework pin
Docker images and dashboard HTML derive their version dynamically from
pyproject.toml at build time, so they need no separate check here.
@@ -105,29 +103,16 @@ def _uv_lock_package(path: Path, pkg: str) -> str:
raise RuntimeError(f"{pkg!r} entry not found in {path}")
-def _alias_framework_pin(path: Path) -> str:
- """The version of the ``admina-framework==X`` dependency of the alias."""
- data = tomllib.loads(path.read_text(encoding="utf-8"))
- for dep in data["project"].get("dependencies", []):
- m = re.fullmatch(r"admina-framework==(\S+)", dep.strip())
- if m:
- return m.group(1)
- raise RuntimeError(f"no admina-framework==X dependency in {path}")
-
-
def main(repo: Path = REPO) -> int:
- alias = repo / "packaging" / "admina" / "pyproject.toml"
versions: dict[str, str] = {
"pyproject.toml": _toml_version(repo / "pyproject.toml"),
"admina/__init__.py": _python_dunder_version(repo / "admina" / "__init__.py"),
- "uv.lock": _uv_lock_package(repo / "uv.lock", "admina-framework"),
+ "uv.lock": _uv_lock_package(repo / "uv.lock", "admina"),
"uv.lock (admina-core)": _uv_lock_package(repo / "uv.lock", "admina-core"),
"core-rust/pyproject.toml": _toml_version(repo / "core-rust" / "pyproject.toml"),
"core-rust/Cargo.toml": _toml_version(repo / "core-rust" / "Cargo.toml", table="package"),
"core-rust/Cargo.lock": _cargo_lock_admina_core(repo / "core-rust" / "Cargo.lock"),
"core-rust/uv.lock": _uv_lock_package(repo / "core-rust" / "uv.lock", "admina-core"),
- "packaging/admina/pyproject.toml": _toml_version(alias),
- "packaging/admina (admina-framework pin)": _alias_framework_pin(alias),
}
canonical = versions["pyproject.toml"]
diff --git a/tests/test_admina_alias.py b/tests/test_admina_alias.py
deleted file mode 100644
index ab63bac..0000000
--- a/tests/test_admina_alias.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# Copyright © 2025–2026 Stefano Noferi & Admina contributors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""The ``admina`` alias package (``packaging/admina``) mirrors admina-framework.
-
-``pip install admina[X]`` installs ``admina-framework[X]`` at the same
-version, for every extra X of admina-framework, and the alias ships no
-module of its own (``import admina`` comes from admina-framework only).
-"""
-
-from __future__ import annotations
-
-import tomllib
-from pathlib import Path
-
-REPO = Path(__file__).resolve().parent.parent
-
-
-def _project(path: Path) -> dict:
- return tomllib.loads(path.read_text(encoding="utf-8"))
-
-
-ROOT = _project(REPO / "pyproject.toml")
-ALIAS = _project(REPO / "packaging" / "admina" / "pyproject.toml")
-
-
-def test_the_alias_installs_admina_framework_at_its_version():
- version = ROOT["project"]["version"]
- assert ALIAS["project"]["name"] == "admina"
- assert ALIAS["project"]["version"] == version
- assert ALIAS["project"]["dependencies"] == [f"admina-framework=={version}"]
-
-
-def test_every_extra_of_admina_framework_has_an_alias_extra():
- version = ROOT["project"]["version"]
- expected = {
- extra: [f"admina-framework[{extra}]=={version}"]
- for extra in ROOT["project"]["optional-dependencies"]
- }
- assert ALIAS["project"]["optional-dependencies"] == expected
-
-
-def test_the_alias_ships_no_module():
- assert ALIAS["tool"]["setuptools"]["packages"] == []
- assert ALIAS["project"]["requires-python"] == ROOT["project"]["requires-python"]
diff --git a/tests/test_admina_framework_sdist.py b/tests/test_admina_framework_sdist.py
new file mode 100644
index 0000000..0e1fd01
--- /dev/null
+++ b/tests/test_admina_framework_sdist.py
@@ -0,0 +1,84 @@
+# Copyright © 2025–2026 Stefano Noferi & Admina contributors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""The last admina-framework release (``scripts/build-admina-framework-sdist.py``).
+
+A source distribution named after the version of ``pyproject.toml`` whose
+``setup.py`` stops the installation with the commands to switch to
+``admina``.
+"""
+
+from __future__ import annotations
+
+import importlib.util
+import os
+import subprocess
+import sys
+import tarfile
+import tomllib
+from email.parser import Parser
+from pathlib import Path
+
+REPO = Path(__file__).resolve().parent.parent
+VERSION = tomllib.loads((REPO / "pyproject.toml").read_text(encoding="utf-8"))["project"]["version"]
+
+
+def _build(out_dir: Path) -> Path:
+ spec = importlib.util.spec_from_file_location(
+ "build_admina_framework_sdist", REPO / "scripts" / "build-admina-framework-sdist.py"
+ )
+ module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(module)
+ return module.build(out_dir)
+
+
+def test_the_archive_is_named_and_laid_out_as_an_sdist(tmp_path):
+ archive = _build(tmp_path)
+ top = f"admina_framework-{VERSION}"
+ assert archive.name == f"{top}.tar.gz"
+ with tarfile.open(archive) as tar:
+ names = sorted(tar.getnames())
+ assert names == sorted(
+ f"{top}/{name}" for name in ("PKG-INFO", "pyproject.toml", "setup.py", "README.md")
+ )
+
+
+def test_the_metadata_names_admina_framework_at_the_project_version(tmp_path):
+ archive = _build(tmp_path)
+ with tarfile.open(archive) as tar:
+ raw = tar.extractfile(f"admina_framework-{VERSION}/PKG-INFO").read().decode()
+ meta = Parser().parsestr(raw)
+ assert meta["Name"] == "admina-framework"
+ assert meta["Version"] == VERSION
+ assert meta["Description-Content-Type"] == "text/markdown"
+ assert "pip install admina" in meta.get_payload()
+
+
+def test_setup_py_stops_with_the_commands_to_switch(tmp_path):
+ archive = _build(tmp_path)
+ with tarfile.open(archive) as tar:
+ tar.extractall(tmp_path, filter="data")
+ result = subprocess.run(
+ [sys.executable, "setup.py", "egg_info"],
+ cwd=tmp_path / f"admina_framework-{VERSION}",
+ capture_output=True,
+ text=True,
+ check=False,
+ # Only PATH: no coverage variables, so the subprocess writes no data
+ # file outside the configuration of the repository.
+ env={"PATH": os.environ.get("PATH", "")},
+ )
+ assert result.returncode != 0
+ assert "admina-framework is now published as admina" in result.stderr
+ assert "pip uninstall -y admina admina-framework && pip install admina" in result.stderr
diff --git a/tests/test_check_versions.py b/tests/test_check_versions.py
index edf29ac..a72729a 100644
--- a/tests/test_check_versions.py
+++ b/tests/test_check_versions.py
@@ -44,38 +44,27 @@ def _write_repo(
cargo: str | None = None,
runtime: str | None = None,
locked_core: str | None = None,
- alias: str | None = None,
- alias_pin: str | None = None,
) -> Path:
"""Write the tracked manifests of a repository at ``version``.
``cargo`` is the crate version (Cargo.toml and Cargo.lock), ``runtime``
the ``__version__`` and ``locked_core`` the admina-core entry of uv.lock;
- each defaults to ``version``. ``alias`` and ``alias_pin`` are the
- version of the ``admina`` alias and its admina-framework pin.
+ each defaults to ``version``.
"""
- alias = alias or version
- alias_pin = alias_pin or version
- (root / "packaging" / "admina").mkdir(parents=True)
- (root / "packaging" / "admina" / "pyproject.toml").write_text(
- f'[project]\nname = "admina"\nversion = "{alias}"\n'
- f'dependencies = ["admina-framework=={alias_pin}"]\n',
- encoding="utf-8",
- )
cargo = cargo or version
runtime = runtime or version
locked_core = locked_core or version
(root / "admina").mkdir()
(root / "core-rust").mkdir()
(root / "pyproject.toml").write_text(
- f'[project]\nname = "admina-framework"\nversion = "{version}"\n', encoding="utf-8"
+ f'[project]\nname = "admina"\nversion = "{version}"\n', encoding="utf-8"
)
(root / "admina" / "__init__.py").write_text(f'__version__ = "{runtime}"\n', encoding="utf-8")
(root / "uv.lock").write_text(
"version = 1\nrevision = 3\n\n"
f'[[package]]\nname = "admina-core"\nversion = "{locked_core}"\n'
'source = { directory = "core-rust" }\n\n'
- f'[[package]]\nname = "admina-framework"\nversion = "{version}"\n'
+ f'[[package]]\nname = "admina"\nversion = "{version}"\n'
'source = { editable = "." }\n',
encoding="utf-8",
)
@@ -139,8 +128,6 @@ def test_aligned_final_release_passes(tmp_path):
({"cargo": "0.13.0"}, "core-rust/Cargo.toml"),
({"runtime": "0.12.1"}, "admina/__init__.py"),
({"locked_core": "0.9.3"}, "uv.lock (admina-core)"),
- ({"alias": "0.12.2"}, "packaging/admina/pyproject.toml"),
- ({"alias_pin": "0.12.2"}, "packaging/admina (admina-framework pin)"),
],
)
def test_real_drift_fails(tmp_path, capsys, overrides, drifting):
diff --git a/tests/test_cli/test_init.py b/tests/test_cli/test_init.py
index b06376b..3297638 100644
--- a/tests/test_cli/test_init.py
+++ b/tests/test_cli/test_init.py
@@ -375,7 +375,7 @@ def test_next_steps_when_proxy_installed_shows_admina_dev(
out = cli_main._format_next_steps("foo")
assert "admina dev" in out
# No upgrade hint when [proxy] is already there.
- assert "pip install 'admina-framework[proxy]'" not in out
+ assert "pip install 'admina[proxy]'" not in out
def test_next_steps_when_proxy_missing_omits_admina_dev_command(
self, monkeypatch: pytest.MonkeyPatch
@@ -392,7 +392,7 @@ def test_next_steps_when_proxy_missing_omits_admina_dev_command(
if stripped == "admina dev" or stripped.startswith("admina dev "):
# Only --stack is acceptable here, and only if docker is around.
assert stripped.startswith("admina dev --stack"), line
- assert "pip install 'admina-framework[proxy]'" in out
+ assert "pip install 'admina[proxy]'" in out
def test_next_steps_python_main_always_shown(self, monkeypatch: pytest.MonkeyPatch) -> None:
from admina.cli import main as cli_main
@@ -419,7 +419,7 @@ def test_require_proxy_extra_exits_with_actionable_message(
captured = capsys.readouterr()
combined = captured.out + captured.err
assert "[proxy]" in combined
- assert "pip install 'admina-framework[proxy]'" in combined
+ assert "pip install 'admina[proxy]'" in combined
assert "Traceback" not in combined
def test_require_proxy_extra_noop_when_installed(self, monkeypatch: pytest.MonkeyPatch) -> None:
diff --git a/tests/test_domains.py b/tests/test_domains.py
index ca56bfb..ea38fb3 100644
--- a/tests/test_domains.py
+++ b/tests/test_domains.py
@@ -290,7 +290,7 @@ def test_pii_factory(self):
def test_pii_factory_without_spacy_installed(self, monkeypatch):
"""Regression: admina dev must boot even when spaCy is not installed.
- Simulates a user who ran `pip install admina-framework[proxy]` without
+ Simulates a user who ran `pip install admina[proxy]` without
the [nlp] extra. The PII bridge must fall back to regex-only mode
instead of crashing the proxy lifespan with ModuleNotFoundError.
"""
diff --git a/tests/test_firewall_parity.py b/tests/test_firewall_parity.py
index 4820fb7..d96d935 100644
--- a/tests/test_firewall_parity.py
+++ b/tests/test_firewall_parity.py
@@ -14,7 +14,7 @@
"""Detection-parity tests between the pure-Python and Rust firewalls.
-The Rust engine is an OPT-IN accelerator (`pip install admina-framework[rust]`).
+The Rust engine is an OPT-IN accelerator (`pip install admina[rust]`).
Because enabling it swaps out the detection engine, its behaviour must not
silently regress below the pure-Python default. These tests pin the parity
contract as of 0.9.4:
diff --git a/tests/test_integrations/test_loop_breaker_dependency.py b/tests/test_integrations/test_loop_breaker_dependency.py
index e1c29b9..8330ac4 100644
--- a/tests/test_integrations/test_loop_breaker_dependency.py
+++ b/tests/test_integrations/test_loop_breaker_dependency.py
@@ -46,7 +46,7 @@ def no_sklearn(monkeypatch: pytest.MonkeyPatch) -> None:
def _assert_actionable(exc: pytest.ExceptionInfo[ImportError]) -> None:
message = str(exc.value)
- assert "admina-framework[proxy]" in message
+ assert "admina[proxy]" in message
assert "loop_detection=False" in message
assert isinstance(exc.value.__cause__, ImportError)
diff --git a/tests/test_lazy_imports.py b/tests/test_lazy_imports.py
index a2df867..449ca5d 100644
--- a/tests/test_lazy_imports.py
+++ b/tests/test_lazy_imports.py
@@ -230,7 +230,7 @@ def test_loop_breaker_surfaces_explain_the_missing_dependencies(tmp_path):
proc = _run(code, tmp_path) # every surface
assert proc.returncode != 0
assert "ADMINA_ENABLED_SURFACES=gateway" in proc.stderr
- assert "admina-framework[proxy]" in proc.stderr
+ assert "admina[proxy]" in proc.stderr
def test_configured_redis_without_the_package_is_a_warning(tmp_path):
diff --git a/tests/test_plugins/test_scaffold_contracts.py b/tests/test_plugins/test_scaffold_contracts.py
index c15e994..ea5376e 100644
--- a/tests/test_plugins/test_scaffold_contracts.py
+++ b/tests/test_plugins/test_scaffold_contracts.py
@@ -82,7 +82,7 @@ def test_scaffolded_pyproject_metadata(tmp_path):
_scaffold_plugin("my-guard", "governance_guard", tmp_path / "my-guard")
text = (tmp_path / "my-guard" / "pyproject.toml").read_text()
- assert f"admina-framework>={__version__}" in text
+ assert f"admina>={__version__}" in text
assert "admina>=0.9.0" not in text
assert 'requires-python = ">=3.11"' in text
assert '[project.entry-points."admina.plugins"]' in text
diff --git a/tests/test_presidio_engine.py b/tests/test_presidio_engine.py
index 0770f11..ba61347 100644
--- a/tests/test_presidio_engine.py
+++ b/tests/test_presidio_engine.py
@@ -32,7 +32,7 @@ def fake_import(name, *args, **kwargs):
monkeypatch.setattr(builtins, "__import__", fake_import)
from admina.engines.presidio import PresidioPIIEngine
- with pytest.raises(ImportError, match=r"admina-framework\[presidio\]"):
+ with pytest.raises(ImportError, match=r"admina\[presidio\]"):
PresidioPIIEngine()
diff --git a/tests/test_redteam_external_corpora.py b/tests/test_redteam_external_corpora.py
index f1f222d..8b88ea6 100644
--- a/tests/test_redteam_external_corpora.py
+++ b/tests/test_redteam_external_corpora.py
@@ -344,7 +344,7 @@ def test_rust_engine_alone_is_refused_without_admina_core(monkeypatch):
redteam.run_suite(engines=["rust"], baseline=redteam.BASELINE_PATH)
assert str(info.value) == (
"the selected engines (rust) cannot run the corpora injection, pii, loop: "
- "admina-core is not installed (install admina-framework[rust])"
+ "admina-core is not installed (install admina[rust])"
)
diff --git a/tests/test_sdk/test_top_level_imports.py b/tests/test_sdk/test_top_level_imports.py
index 3d7280f..154a6c5 100644
--- a/tests/test_sdk/test_top_level_imports.py
+++ b/tests/test_sdk/test_top_level_imports.py
@@ -67,7 +67,7 @@ class TestSDKOnlyImport:
"""SDK-only install must not require any [proxy]/[nlp]/[telemetry] extras.
A user following the README's "SDK only (lightweight)" path runs
- `pip install admina-framework` (no extras) and then
+ `pip install admina` (no extras) and then
`from admina import GovernedModel`. That import chain must not
transitively pull in boto3, spacy, sklearn, numpy, or any other
optional dependency, otherwise the lightweight install is broken.
diff --git a/tests/test_sdk/test_without_numpy.py b/tests/test_sdk/test_without_numpy.py
index 93c166c..72867d5 100644
--- a/tests/test_sdk/test_without_numpy.py
+++ b/tests/test_sdk/test_without_numpy.py
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""The SDK installed without the scientific stack (``pip install admina-framework``).
+"""The SDK installed without the scientific stack (``pip install admina``).
numpy and scikit-learn come with the ``proxy`` extra, for the Python loop
breaker. Without them, ``GovernedModel.ask()`` governs a prompt (firewall,
@@ -89,4 +89,4 @@ def test_loop_detection_without_numpy_names_the_extra():
)
assert proc.returncode == 0, proc.stderr
assert "ImportError:" in proc.stdout
- assert "admina-framework[proxy]" in proc.stdout
+ assert "admina[proxy]" in proc.stdout
diff --git a/uv.lock b/uv.lock
index 6a7ce8c..ce5d5a2 100644
--- a/uv.lock
+++ b/uv.lock
@@ -9,13 +9,8 @@ resolution-markers = [
]
[[package]]
-name = "admina-core"
-version = "0.13.2"
-source = { directory = "core-rust" }
-
-[[package]]
-name = "admina-framework"
-version = "0.13.2"
+name = "admina"
+version = "0.14.0"
source = { editable = "." }
dependencies = [
{ name = "click" },
@@ -148,10 +143,10 @@ dev = [
[package.metadata]
requires-dist = [
+ { name = "admina", extras = ["openai", "ollama", "anthropic", "mistral", "gemini", "bedrock"], marker = "extra == 'adapters'" },
+ { name = "admina", extras = ["proxy", "nlp", "telemetry"], marker = "extra == 'full'" },
+ { name = "admina", extras = ["proxy", "nlp", "telemetry", "adapters", "presidio"], marker = "extra == 'all'" },
{ name = "admina-core", marker = "extra == 'rust'", directory = "core-rust" },
- { name = "admina-framework", extras = ["openai", "ollama", "anthropic", "mistral", "gemini", "bedrock"], marker = "extra == 'adapters'" },
- { name = "admina-framework", extras = ["proxy", "nlp", "telemetry"], marker = "extra == 'full'" },
- { name = "admina-framework", extras = ["proxy", "nlp", "telemetry", "adapters", "presidio"], marker = "extra == 'all'" },
{ name = "anthropic", marker = "extra == 'anthropic'", specifier = ">=0.39,<2" },
{ name = "boto3", marker = "extra == 'bedrock'", specifier = ">=1.34,<2" },
{ name = "boto3", marker = "extra == 'proxy'", specifier = ">=1.34,<2" },
@@ -202,6 +197,11 @@ dev = [
{ name = "safety", specifier = ">=3.0,<4" },
]
+[[package]]
+name = "admina-core"
+version = "0.14.0"
+source = { directory = "core-rust" }
+
[[package]]
name = "annotated-doc"
version = "0.0.4"