Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ jobs:
TAG: ${{ needs.release-please.outputs.tag_name }}
run: |
set -euo pipefail
cp "$BUNDLE" "dist/aptl-labs-${TAG}.intoto.jsonl"
cp "$BUNDLE" "dist/lilrae-${TAG}.intoto.jsonl"
gh release upload "$TAG" dist/* sbom/* --clobber

backmerge:
Expand All @@ -119,13 +119,24 @@ jobs:
# main is now ahead of dev. Auto-open a back-merge PR so dev stays
# current. It's opened by GITHUB_TOKEN, so dev's required checks won't
# run on it — admin-merge it (one click).
#
# TAG goes through the environment, so no workflow expression is parsed
# as shell. Only "dev already contains main" ends without a PR; any
# other failure (auth, network, API) fails the job.
env:
TAG: ${{ needs.release-please.outputs.tag_name }}
run: |
set -euo pipefail
if [ "$(gh pr list --base dev --head main --state open --json number --jq 'length')" != "0" ]; then
open_prs="$(gh pr list --base dev --head main --state open --json number --jq 'length')"
if [ "$open_prs" != "0" ]; then
echo "back-merge PR main->dev already open; nothing to do."
exit 0
fi
ahead="$(gh api "repos/${GH_REPO}/compare/dev...main" --jq '.ahead_by')"
if [ "$ahead" = "0" ]; then
echo "dev already contains main; no back-merge needed."
exit 0
fi
gh pr create --base dev --head main \
--title "chore: back-merge ${{ needs.release-please.outputs.tag_name }} into dev" \
--body "Automated back-merge of the release commit (version bump + CHANGELOG.md) from \`main\` into \`dev\` so dev stays current after the release. Admin-merge it — dev's required checks don't run on a bot-opened PR." \
|| echo "no back-merge needed (dev already up to date with main)."
--title "chore: back-merge ${TAG} into dev" \
--body "Automated back-merge of the release commit (version bump + CHANGELOG.md) from \`main\` into \`dev\` so dev stays current after the release. Admin-merge it — dev's required checks don't run on a bot-opened PR."
29 changes: 14 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@

# LilRAE (formerly APTL)

> **APTL is now LilRAE, and `aptl-labs` 7.0.0 is planned as the last
> `aptl-labs` release.** The Advanced Purple Team Lab (APTL) repository moved to
> **APTL is now LilRAE, and the package is now `lilrae`.** The Advanced Purple
> Team Lab (APTL) repository moved to
> [github.com/OpenRAE/lilrae](https://github.com/OpenRAE/lilrae) and was
> renamed. It is the same project, with the same source, history, features,
> and issue tracker. Later releases will publish to PyPI as
> [`lilrae`](https://pypi.org/project/lilrae/), starting at version 8.0.0. The
> import package is still `aptl`, the command is still `aptl`, and existing
> configuration, state directories, and runtime identifiers keep their `aptl`
> names. Where current pages still say APTL, they mean LilRAE. See
> and issue tracker. Releases publish to PyPI as
> [`lilrae`](https://pypi.org/project/lilrae/) from version 8.0.0.
> `aptl-labs` 7.0.0 was the last `aptl-labs` release. The import package is
> still `aptl`, the command is still `aptl`, and existing configuration, state
> directories, and runtime identifiers keep their `aptl` names. Where current
> pages still say APTL, they mean LilRAE. See
> [ADR-062](https://github.com/OpenRAE/lilrae/blob/main/docs/adrs/adr-062-lilrae-name-and-naming-convention.md).
>
> Do not change packages until `lilrae` 8.0.0 or later shows on its PyPI page.
> Until then, keep `aptl-labs`. The two distributions install the same `aptl`
> package and commands. Do not install `lilrae` over `aptl-labs`, because one
> installation overwrites the files of the other. Remove `aptl-labs` first, then install `lilrae` with the
> The two distributions install the same `aptl` package and commands. Do not
> install `lilrae` over `aptl-labs`, because one installation overwrites the
> files of the other. Remove `aptl-labs` first, then install `lilrae` with the
> same extras.
>
> With pipx:
Expand All @@ -37,15 +37,14 @@
> python -m pip install "lilrae[s3]"
> ```
>
> To roll back after a migration, remove `lilrae`, then install the last legacy
> release:
> To roll back, remove `lilrae`, then install the last legacy release:
>
> ```shell
> pipx uninstall lilrae
> pipx install "aptl-labs==7.0.0"
> ```
>
> The [installation guide](https://github.com/OpenRAE/lilrae/blob/main/docs/getting-started/installation.md#move-to-the-lilrae-package)
> The [installation guide](https://github.com/OpenRAE/lilrae/blob/main/docs/getting-started/installation.md#move-from-aptl-labs-to-lilrae)
> has the full procedure. All earlier `aptl-labs` releases stay on PyPI.

LilRAE is a local purple-team lab where human operators and AI agents exercise
Expand Down Expand Up @@ -89,7 +88,7 @@ Install the released CLI and materialize its bundled lab assets. No source
checkout is required:

```shell
pipx install aptl-labs
pipx install lilrae
aptl lab init my-lab
cd my-lab
aptl lab start --scenario techvault
Expand Down
2 changes: 1 addition & 1 deletion appliance/guest/provision-offline.sh
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ PYTHONPATH="$1" python3 -m pip install --no-index --only-binary=:all: \
# Keep the application wheel separate from the dependency closure. pip's
# --target install does not merge an existing bin directory, so installing the
# wheel into the dependency target would silently omit its aptl entrypoint.
set -- "$payload_dir"/wheelhouse/aptl_labs-*.whl
set -- "$payload_dir"/wheelhouse/lilrae-*.whl
test "$#" -eq 1
test -f "$1"
PYTHONPATH="$pip_wheel" python3 -m pip install \
Expand Down
20 changes: 20 additions & 0 deletions docs/adrs/adr-062-lilrae-name-and-naming-convention.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,26 @@ A future change to any of these needs its own compatibility and migration
decision. No alias package, compatibility shim, second import root, duplicate
configuration schema, or name toggle is introduced.

### Successor decision: distribution from 8.0.0 (#1367)

Issue #1367 supersedes only the retained `aptl-labs` distribution clause and
current distribution-related install, upgrade, and recovery wording above.
`aptl-labs` 7.0.0 is the final legacy release; the successor distribution is
`lilrae` from 8.0.0. This decision authorizes the cutover; it does not assert
that 8.0.0 has published. Release-please continues to own version selection.

The successor ships the same import packages, commands, entry points, extras,
and bundled asset contract. All other executable, configuration, runtime,
schema, and persisted identities remain unchanged. Installed-provider
provenance records the actual distribution; historical evidence, fixtures,
and the closed legacy startup-reset receipt classifier retain their names.
Migration removes the old distribution before installing the successor;
rollback removes the successor before restoring pinned `aptl-labs==7.0.0`.
No alias package or dual installation is supported. The
[#1367 preflight](../architecture/issue-1367-distribution-cutover-preflight.md)
records the release and consumer boundaries; #1234 owns post-release checks
and legacy archival.

### Naming convention for new code

Use `lilrae` as the naming convention for new code going forward. A new
Expand Down
81 changes: 81 additions & 0 deletions docs/architecture/issue-1367-distribution-cutover-preflight.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Issue #1367: Distribution Cutover Preflight

The supplied #1367 issue is the contract; no GRC workflow platform requirement is
attached. This note supplements the [parent preflight](issue-1234-pypi-transition-preflight.md),
whose cross-cutting guardrails remain applicable. It is design guidance, not
an implementation plan or evidence of publication.

## Boundary and canonical owners

Change the Python distribution to `lilrae` for the release following
`aptl-labs` 7.0.0. The issue identifies the unreleased breaking fix #1269 as
the reason that release is 8.0.0. Retain the single release-please version/tag
stream; do not manually bump versions or `CHANGELOG.md`, reset numbering, or
make a `build:` change trigger releases by inventing a functional commit.
ADR-062's successor clause is limited to distribution identity and its current
user-facing wording; it does not accept the proposed product split in ADR-054.

| Layer | Required incumbent and passage |
| --- | --- |
| Package and build shape | `pyproject.toml` owns name, Python constraint, both `src/aptl` and `src/aptl_techvault` packages, scripts, extras, and `aptl.*` entry points. Preserve these contracts apart from name. `hatch_build.py`, `_asset_manifest.py`, and `core/assets.py` remain the shared build/materialization boundary, including tracked-file selection, secret/state exclusions, sanitized Git environment, full-build marker, and no-Git sdist fallback. Container builds also consume the bundled project metadata; retain their minimal-build behavior. |
| Lock and release shape | `.pre-commit-ci.yaml` owns the pinned UV lock/export hooks and hashed `requirements/*.txt`; use the specific hooks, not the whole-tree CI configuration locally. Avoid unrelated dependency upgrades. `release-please-config.json` must select the one renamed root lock package and retain the `src/aptl/__init__.py` updater. Its `package-name: aptl` is a release component identity, not the wheel's metadata name. `.release-please-manifest.json` and `docs/releasing.md` remain version/process authorities. |
| Installed version and evidence | CLI `--version` reads `aptl.__version__`, maintained by release-please; `core/snapshot.py` reads installed distribution metadata. They must agree in an installed artifact. Keep `SoftwareVersions.aptl_version`, catch only `PackageNotFoundError` for the existing source-only `dev` fallback, and never report `dev` when `lilrae` metadata exists. No legacy-name fallback chain, new version service, or renamed snapshot field. |
| Provider shapes and reset authority | Existing `backends/*_discovery.py`, `backends/scenario_startup.py`, and `validation/scenario_verification_discovery.py` validate installed providers and retain actual metadata provenance. Keep selectors and ambiguity rejection. `startup_reset_state.py`, `lifecycle_cleanup.py`, and `_scenario_startup_runtime.py` own canonical receipt parsing, hashes, safe persistence, and exact pack/reset authorization. Preserve the closed legacy classifier and historical bytes; a renamed distribution grants no new reset authority. An unavailable legacy reset stays a bounded diagnostic under existing rules. |
| Offline guest and host OS | `appliance/versioning.py:aptl_wheel_version(prefix=...)` and `is_appliance_version()` are the existing bounded parsing seam. `build-seat-image.sh` owns application wheel/hash requirement generation; `provision-offline.sh` owns archive shape/size/type admission and hash-enforced installation. Require exactly one matching wheel and matching name/version/hash requirement; zero, duplicate, or tampered inputs fail. Keep `--no-index`, `--no-deps` for the app, separate app/dependency targets, and fixed `/opt/aptl` launchers. `aptl-wheel-requirements.txt` is a payload interface filename, not distribution metadata. Preserve base/system-package pins and seat build-record/signature/trust gates; Python publishing stays independent of seat publishing. |
| Workflow auth and shell boundary | `.github/workflows/release-please.yml` retains pinned actions, `environment: pypi`, job-scoped OIDC/attestation permissions, hashed tooling, SBOM, and attest-before-publish ordering. Bind workflow outputs through `env:` and quoted expansions, including the complete back-merge title; environment binding prevents shell-code interpolation but does not replace metadata/path validation. Keep `GH_TOKEN` in the environment, never argv, shell tracing, logs, or artifacts; add no PyPI token fallback. Account-side `lilrae` trusted-publisher authorization requires external verification under the established release process. |
| Runtime config and auth shapes | `core/config.py:AptlConfig` and its strict nested models, `core/env.py:EnvVars`/placeholder validation, startup environment-binding contracts, and `api/deps.py:WebAuthSettings`/token checks retain their current shapes. No `LILRAE_*` aliases for existing `APTL_*`, new credentials, listeners, principals, config fields, or renamed Compose resources. Existing BFF/MCP validation and auth continue unchanged. |
| Error envelopes and observability | Reuse domain errors, startup reason codes/diagnostics, typed API/MCP envelopes, `utils/logging.py:get_logger()`, and `utils/redaction.py` (ADR-029). Change recovery prose to the installable distribution without changing error types, telemetry identities, or exposing raw validator values/backend stderr. Hash or selection failures remain fatal; no broad exception-to-`dev` conversion or migration exception hierarchy. |

The extensibility seam is **distribution metadata separate from runtime
identity**. The host hash writer already reads `[project]`; derive its name and
normalized wheel prefix there, keeping filename normalization distinct from
the requirement's metadata name. Reuse the existing parser's prefix parameter
and explicit guest artifact selection. Release-please's declarative selector
must match the root metadata. No brand registry, configurable publication
destination, additional runtime config, or duplicated wheel/version parser is
needed for this single successor distribution.

## Gotchas and verification boundaries

The parent identified two existing release hazards still present in this
checkout: publish checkout follows a moving branch rather than the release
tag, and `gh pr create ... || echo` treats every back-merge failure as
"already up to date." Candidate provenance must bind to the release commit;
authentication/network failures must not count as successful workflow work.
Retain these as release review concerns, not a reason to add a second workflow.
Keep attestations outside the PyPI upload set and distinguish the new bundle
filename from stable labels such as `sbom/aptl.cdx.json`.

Reuse `test_version_consistency.py`, `test_version.py`, `test_snapshot.py`,
`test_hashed_requirements.py`, `test_hatch_build_hook.py`, `test_assets.py`,
`test_seat_image_build.py`, `test_appliance_release_workflow.py`, installed
provider/cleanup tests, and `test_docs_user_journey.py`. Existing tests do not
prove all acceptance criteria: the seat test asserts the old glob, and
`aptl_wheel_version()` currently has no repository call sites. Update current
behavior assertions and cover the parser directly; leave historical fixtures
unchanged. Require meaningful zero/multiple-wheel and hash-mismatch cases,
wheel and sdist name/version/content contracts, and installed CLI/snapshot
agreement outside checkout/PYTHONPATH masking. Asset equivalence means the same
asset set and runtime contract, not byte identity of intentionally changed
bundled metadata or messages. Reuse the clean-wheel lifecycle job in
`checks.yml`; retain minimal-container and sdist-rebuild coverage.

Current install/upgrade guidance includes README, docs home/installation,
secondary operator guides, asset recovery text, and `aptl_techvault` package
documentation. Preserve uninstall-first pip and pipx migration, pinned
rollback, extras/interpreter choice, and state. Do not advertise an unpublished
successor as verified, use `--force` to hide command collisions, or rewrite
old reviews, ADR history, evidence, and fixtures by global replacement.

Local verification uses explicit tests through `tools/run-targeted-tests.sh`
and staged-file `pre-commit run`; full tests, prose/docs gates, and clean lab
checks remain CI/CD-owned. No requirement UID, status, or traceability is
fabricated. #1234 owns post-publication clean install, migration, rollback,
CLI smoke evidence and archival. Source inspection cannot verify PyPI account
state or publication, and this preflight authorizes no external mutation.

## Non-goals

No issue implementation, package/import/CLI aliases, runtime rename, schema
or persistence migration, feature extraction, dependency overhaul, alternate
build backend, second release pipeline, publishing, archival, or data transfer.
2 changes: 1 addition & 1 deletion docs/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ raw Compose commands are not an equivalent deployment path.
Install the released package and materialize its lab assets:

```shell
pipx install aptl-labs
pipx install lilrae
aptl lab init my-lab
cd my-lab
aptl lab start --scenario techvault
Expand Down
29 changes: 14 additions & 15 deletions docs/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@ pipx environment. Check the [prerequisites](prerequisites.md) first.

Install pipx through your operating system or the
[official pipx instructions](https://pipx.pypa.io/stable/installation/), then
install LilRAE. LilRAE was formerly named APTL; it is still published as the
`aptl-labs` package and provides the `aptl` command
install LilRAE. LilRAE was formerly named APTL. It is published as the
`lilrae` package and provides the `aptl` command
([ADR-062](../adrs/adr-062-lilrae-name-and-naming-convention.md)):

```shell
pipx install aptl-labs
pipx install lilrae
aptl --version
```

`aptl-labs` requires Python 3.12 or newer. On macOS, if pipx is bound to the
`lilrae` requires Python 3.12 or newer. On macOS, if pipx is bound to the
Command Line Tools Python 3.9, ask pipx to fetch a supported interpreter:

```shell
pipx install --python 3.12 --fetch-missing-python aptl-labs
pipx install --python 3.12 --fetch-missing-python lilrae
```

pipx keeps the CLI outside the system Python environment, avoiding the PEP 668
Expand Down Expand Up @@ -52,20 +52,19 @@ Upgrade the isolated installation, then initialize a new project when you want
the complete asset set from the new release:

```shell
pipx upgrade aptl-labs
pipx upgrade lilrae
aptl lab init my-new-lab
```

Do not copy generated credentials or state between project directories.

## Move To The lilrae Package
## Move From aptl-labs To lilrae

`aptl-labs` 7.0.0 is planned as the last `aptl-labs` release. Later releases
will publish to PyPI as [`lilrae`](https://pypi.org/project/lilrae/), starting
at version 8.0.0. Do not start a migration until `lilrae` 8.0.0 or later shows
on its PyPI page. Until then, keep `aptl-labs` and upgrade it as usual. The import package, the `aptl` and `aptl-misp-suricata-sync` commands,
the configuration, and the project state do not change. `pipx upgrade
aptl-labs` does not move you to `lilrae`.
`aptl-labs` 7.0.0 was the last `aptl-labs` release. Releases publish to PyPI
as [`lilrae`](https://pypi.org/project/lilrae/) from version 8.0.0. The import
package, the `aptl` and `aptl-misp-suricata-sync` commands, the configuration,
and the project state do not change. `pipx upgrade aptl-labs` does not move
you to `lilrae`.

Both distributions install the same `aptl` package and the same commands. If
you install one over the other, the second installation overwrites the files of
Expand Down Expand Up @@ -130,8 +129,8 @@ installation finds it.

### Roll Back

To return to the legacy package after a migration, remove `lilrae` first, then
install the last legacy release:
To return to the legacy package, remove `lilrae` first, then install the last
legacy release:

```shell
pipx uninstall lilrae
Expand Down
2 changes: 1 addition & 1 deletion docs/getting-started/installing-packs.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ The adapter registers entry points in these groups, keyed by the pack identity:
| `aptl.evidence_payload` | Evidence collectors |
| `aptl.participant_mcp_smoke_plans` | Participant MCP smoke plans |

The TechVault adapter is `aptl_techvault`. It ships inside the `aptl-labs`
The TechVault adapter is `aptl_techvault`. It ships inside the `lilrae`
(LilRAE) distribution, so a LilRAE install already has it. You do not install it
separately.

Expand Down
Loading
Loading