feat(basilica): opt-in admin key rotation after bootstrap#238
Merged
Conversation
Add `rotate_admin_key()` primitive + `rotate_admin_after_bootstrap` TenantSpec field (default off) so callers can invalidate the bootstrap admin key as soon as `provision()` returns. Wire it through the CLI as the `rotate-admin-key` subcommand and the tenant-lifecycle workflow as a new `action` choice. The workflow `::add-mask::`s `admin_key` before any `cat result.json` line, same pattern as the existing `api_key` mask. Mechanism: the Basilica SDK exposes no env-patch primitive (create/delete/restart only). Rotation therefore deletes the existing proxy UUID and creates a fresh one with the rotated env. As a consequence, `proxy_instance_id` and `proxy.url` change; the result JSON returns the post-rotation values which the caller must persist. Trade-off: opt-in because the rotation adds one proxy re-roll (~30s) to provisioning time. Recommended for production tenants where the bootstrap admin key must be invalidated; safe to leave off for dev / sandbox tenants. Tests: 9 unit tests under deployments/basilica/tests/ exercising the rotation logic with the Basilica SDK HTTP boundary mocked (create_deployment / delete_deployment / get_deployment). Rotation logic itself runs unmodified.
501319c to
197d0aa
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
rotate_admin_key()primitive indeployments/basilica/lifecycle.pyand a newTenantSpec.rotate_admin_after_bootstrap: bool = Falsefield (default off). When enabled,provision()runs the rotation as a post-step so the bootstrap admin key returned to the caller is invalidated by the time provisioning returns.python -m deployments.basilica.cli rotate-admin-key --tenant-id ... --config ... --proxy-instance-id ...(with optional--new-key), and through thetenant-lifecycleworkflow as a newaction: rotate-admin-keychoice (workflow_dispatch + repository_dispatch).::add-mask::s the returnedadmin_keyBEFORE anycat result.jsonline, same pattern as the existingapi_keymask; emits it as a step output for the dispatching app to consume via the Actions API.Mechanism
The Basilica SDK exposes no env-patch primitive — only
create_deployment,delete_deployment, andrestart_deployment(which rolls pods without touching env). Rotation therefore deletes the existing proxy UUID and creates a fresh one withLLMTRACE_AUTH_ADMIN_KEY=<new_key>in its env. Consequence:proxy_instance_idandproxy.urlchange. Callers MUST persist the post-rotationproxy_instance_id/proxy_urlover the bootstrap values; both are returned in the result JSON.This deviates slightly from the literal task description (which assumed an in-place env patch); I chose to be honest about the SDK reality and document it explicitly in code + README rather than fake an env-patch path that doesn't exist on the SDK. The added function arg
proxy_spec: ComponentSpecis required for the same reason — Basilica'sget_deploymentdoes not echo env back, so we cannot recover the existing env shape and must use the caller's spec.Trade-off
Opt-in because the rotation adds one proxy re-roll (~30s) to provisioning time. Recommended for production tenants where the bootstrap admin key must be invalidated immediately; leave off for dev / sandbox tenants.
Validation evidence
python3 -c \"from deployments.basilica import lifecycle, cli; print('ok')\"— pass (withbasilicaSDK on PYTHONPATH).python3 -m unittest deployments.basilica.tests.test_rotate_admin_key— 9 tests pass. Tests mock only the SDK HTTP boundary (BasilicaClient.create_deployment/delete_deployment/get_deployment); the rotation logic itself runs unmodified. Covered: fresh key generated and injected; caller-suppliednew_keyhonoured; old proxy deleted then new created; rejection of emptyproxy_instance_id; tenant-id validation; customproxy_name_template; defaultrotate_admin_after_bootstrap=False; rotation skipped whenenable_proxy_auth=False; full provision→rotation flow returns the new key + new UUID.What's unvalidated
BASILICA_API_TOKEN, no live tenant). Live rotation tested by maintainer after merge.Files
deployments/basilica/lifecycle.py—rotate_admin_key(),RotationResult,TenantSpec.rotate_admin_after_bootstrap, post-step inprovision().deployments/basilica/cli.py—rotate-admin-keysubcommand + config wiring for the new field.deployments/basilica/tests/test_rotate_admin_key.py— 9 unit tests..github/workflows/tenant-lifecycle.yml—action: rotate-admin-keychoice,new_admin_keyinput, validation + arg wiring,admin_keymask + step output.deployments/basilica/README.md— new "Admin key rotation" subsection under auth.deployments/basilica/configs/examples/starter.yaml,pro.yaml— opt-in flag comments.Test plan
rotate-admin-keydispatch against an existing tenant; confirm the new proxy reachesphase=ready, the new URL returns 401 for the old key and 200 for the new, and the workflow step outputadmin_keyis masked in run logs but available via the Actions API.provisionwithrotate_admin_after_bootstrap: truein the YAML; confirm the returnedapi_keyis not the bootstrap value (which only lives in the lifecycle library's process memory).