Feature Specification: Deployment Lifecycle
Created: 2026-09-23
Status: Implemented
Input: Reconstructed from the code at 0bf4ecd36 (panel/src/panel/views.py, models.py, forms.py, tokens.py, api_client.py, templates/deployment_list.html, templates/deployment_form.html, static/deployment_submit.js, static/deployment_delete.js, static/centrifuge_session.js), api/specification.md (sections "Two-store model and deletion" and "Browser-orchestrated delete"), pull requests #897, #969, #1011, #1036, #1052, #1186, #1311, #1339, #1777, #1839 and #1842, and the design notes of the maintainers.
User Scenarios & Testing (mandatory)
User Story 1 - Keep a draft and deploy it (Priority: P1)
An operator makes a deployment, edits its configuration, and saves it as a
draft. The operator can save an incomplete draft. When the operator deploys,
the browser validates the value, saves it, and submits it to api with a
bearer token. The page follows the build until it ends, and shows the last
lines of the log on a failure.
Why this priority: this is the main task of the panel.
Independent Test: in apps-panel, an operator logs in, makes a
deployment, deploys it, and sees a final state.
Acceptance Scenarios:
- Given a new deployment, When the operator makes it, Then it is "Untitled", has no value, and opens in the form.
- Given a draft, When the operator saves it, Then the panel stores the value, and the page says "Configuration saved.". A JSON request gets
{ok: true}, or400with the error. - Given a valid draft, When the operator deploys, Then the browser submits
{configuration, mode, module_source}toapifor the identifierstr(pk), and waits foractive,idleorfailed. - Given a build that fails, When the page gets the final state, Then the page shows the last 40 lines of the log as text, and the operator can deploy again.
- Given a full build queue, When the operator deploys, Then the page shows a hint for
fediversity.error.queue_full.
User Story 2 - See the state of all my deployments (Priority: P1)
The list shows the deployments of the operator with their state, domain and
last build, from api. The states change live through the event stream.
Why this priority: the operator must see what is running and what failed.
Independent Test: run panel-python-tests (test_deployment_list.py,
test_deployment_state.py, test_api_down.py).
Acceptance Scenarios:
- Given deployments with builds, When the operator opens the list, Then each row shows the state, the domain and the last build that
apigives. - Given a running build, When
apipublishes a new state, Then the row changes without a reload. - Given that
apidoes not answer, When the operator opens the list, Then the rows show "unknown" and the page shows a warning.
User Story 3 - Delete a deployment and its infrastructure (Priority: P1)
The operator confirms the delete. The browser asks api to destroy the
infrastructure, waits until the destroy is done, and then removes the record
in the panel. If the browser stops before the end, api calls the reconcile
callback of the panel when the destroy is done.
Why this priority: a delete that removes the record but not the infrastructure is a leak that nobody sees.
Independent Test: run panel-python-tests (test_deployment_delete.py)
and the JavaScript tests (deployment-delete.test.js).
Acceptance Scenarios:
- Given a deployed deployment, When the operator confirms the delete, Then the browser sends
DELETEtoapiwith a callback URL and a callback token, and waits foridle. - Given a destroy that succeeds, When the browser sees
idle, Then it confirms the delete to the panel, and the panel removes the record. - Given a destroy that fails, When the browser sees
failed, Then the record stays, and the operator can try again. - Given a deployment that
apidoes not know, When the operator deletes it, Thenapianswers404, and the panel removes the record. - Given that the browser closed during a destroy, When
apicalls the reconcile callback with the correct token, Then the panel removes the record. A wrong token gets403.
User Story 4 - Duplicate a deployment (Priority: P3)
The operator copies a deployment to start a new one with the same name, value and module source pin.
Independent Test: run panel-python-tests (test_deployment_form.py).
Acceptance Scenarios:
- Given a deployment, When the operator duplicates it, Then the panel makes a new record with the same value, which
apidoes not know yet.
User Story 5 - Do an action of api on a deployment (Priority: P2)
The operator suspends or resumes a deployment, or does another action that api offers. api names the actions in the actions of deployment_get ("Deployment actions" in api/specification.md), thus a new action of api needs no new code in the panel.
Independent Test: run panel-python-tests (test_deployment_form.py, test_deployment_application_form.py) and panel-js-tests (deployment-action.test.js).
Acceptance Scenarios:
- Given a deployment that
apican suspend, When the operator opens the deployment form, Then the form shows a Suspend button with the description ofapias its tooltip. - Given that the operator clicks the button and accepts the confirmation, When the build of the action succeeds, Then the page reloads and shows the actions of the new state.
- Given a suspended deployment, When the operator opens the deployment form or the form of one application, Then Deploy is disabled and the page shows why. Only the deployment form shows Resume.
Edge Cases
- A save does not change the module source pin, and ignores a pin in the request. The pin is not visible in the form.
- The event stream stops during a deploy: the page shows a warning, not a failure, and polls the state.
apidoes not know a deployment yet: the state proxy answersbuildingwithknown: false.- The browser gets events of an earlier build: the page acts only on the events of its own build.
Requirements (mandatory)
Functional Requirements
- FR-001: The panel MUST store the draft value, the name, the owner and the module source pin of a deployment, and MUST accept an incomplete draft.
- FR-002: The panel MUST send the deploy and the delete from the browser to
api, through the public HTTP interface ofapi, with a bearer token of the panel. - FR-003: The panel MUST get the state, the domain and the builds of a deployment from
api, and MUST NOT store them. - FR-004: The panel MUST remove the record of a deployment only after
apireports that the destroy is done, or that it does not know the deployment. - FR-005: The reconcile callback MUST accept only a token that the panel made for that deployment.
- FR-006: The panel MUST show an "unknown" state, not an error page, when
apidoes not answer. - FR-007: Duplicate MUST copy the name, the value and the module source pin, also when the form does not show the pin.
- FR-008: The deployment form MUST show a control for each action of
deployment_getother thandeployanddelete, and MUST disable Deploy whileapirefuses a deploy.
Key Entities
- Deployment: identifier, owner, name, draft value, module source pin, time of creation.
- Reconcile token: a signed token for one deployment that lets
apiconfirm a destroy. - Build state:
active,building,deploying,idle,failed,destroyingorunknown, asapireports it.
Success Criteria (mandatory)
Measurable Outcomes
- SC-001: A delete never removes the record of a deployment whose infrastructure
apistill has. - SC-002: The list stays usable when
apiis down.
Assumptions
apienqueues a destroy forDELETE, and does not destroy at once.- Save and submit are two steps. A submit that fails after a save leaves a saved draft.
Known Deviations
None.