Actions and results
What each Platform and Apps button and automatic behaviour does, with worked numbers and what the system refuses.
On this page (24)
OverviewPackagesPackage recordCapabilities / AppsExtension recordRelease recordEnvironment recordScheduled jobsJob recordRun recordJob healthRestore drillsDrill recordRecovery evidenceAlertsAlert policy recordFlag recordBills of materialsVulnerability advisoriesPlatform rolesPlatform audit trailCapabilitiesWorkloadRecord screens
Overview
| Action | When | What you do | What happens |
|---|---|---|---|
| Overview tiles match their lists | Some packages, releases, runs, alerts and flags exist | Open Platform > Overview; note each tile; click each tile | Packages awaiting review = Draft + Reviewed packages; Drifted = packages marked drifted; Releases in flight = Draft/Reviewed/Staged/Approved (max 8 listed); Jobs queued = Queued + Waiting to retry runs; Dead letters = Dead runs; Open alerts = Open + Acknowledged; Flags waiting = Proposed flags + active flags with a pending change; Active extensions shown as active/total. Each tile opens the matching list |
| Last good backup tile colour | Backups module has a succeeded backup | Read the Last good backup tile; compare with the newest succeeded backup time | Shows the age in hours; red when there is none ('never') or it is older than 26 h, green otherwise |
| Server banner | Staging server | Open the Overview on https://141-145-152-10.sslip.io | Banner shows the tier label (Staging) and the current environment code if one is marked 'This server'; 'Outgoing mail and webhooks off' shows on a staging/recovery copy unless outbound is allowed; 'Schema is not at this build's head' shows only when the live migration head differs |
Packages
| Action | When | What you do | What happens |
|---|---|---|---|
| Sync from this build | Operator role | Packages > Sync from this build | Note 'Registered n, refreshed n, drifted n, unchanged n.'; every modules/*/module.json is a Draft package with source 'Build manifest', licence 'Proprietary (a2NSoft)', its SHA-256 and file count; running it again with no code change gives 'Registered 0, refreshed 0, drifted 0' and all unchanged |
| Sync never rewrites a reviewed version | A Reviewed or Certified package whose files are later changed on the server | Sync from this build after the files change | The package is NOT updated; it is marked DRIFTED (Digest column shows the warning mark, Overview Drifted tile +1), history shows 'package.drift'; when files return, 'package.drift_cleared' |
| Register an uploaded package | Operator | Register a package: Code X_DEMO, Version 1.0.0, Title, 64-hex SHA-256, Licence MIT, Support owner; Save | Created as Draft, source Uploaded, kind custom, licence review Pending, files 0; history 'package.registered' |
Package record
| Action | When | What you do | What happens |
|---|---|---|---|
| Edit only a draft | A Draft and a Reviewed package | Edit the draft's dependencies and save; try to edit the Reviewed one (API PUT) | Draft saves and revision +1; Reviewed: Edit not offered; API -> 'A reviewed or certified version cannot change. Register a new version.' |
| Licence review | Approver role; Draft package | Licence review > Decision Approved > OK; repeat with Rejected and no note | Licence review = Approved; with Rejected the note is required ('This is required.'); on a Certified/Retired version -> 'This version is already certified or retired.' |
| Review refused until every check passes | Draft package with licence Pending and no support owner | Open Review checks tab; press Review | Tab lists each problem; Review refused with the first: 'Licence review is pending; it must be approved first.' then 'Name the support owner or maintainer.'; a dependency not available -> 'X_DEMO 1.0.0 needs FIN >=9.0, which is not available as a reviewed version.' |
| Dependency cycle refused | Draft A depends on B; reviewed B depends on A | Review A | Refused 'Dependency cycle: A -> B -> A.' (arrows shown as the arrow sign); A stays Draft (PLT T001) |
| Core range check | Draft package with Core versions = 2 | Review it | Refused 'X_DEMO 1.0.0 needs core 2; this build is core 1.0.0.'; with core 1 or >=1.0, <2.0 it passes |
| Review then certify by another person | User A release manager, user B approver; package with all checks passing | A presses Review; A (if also approver) tries Certify; B presses Certify | Review -> Reviewed (reviewed by A); A's Certify refused 'Somebody other than the person who prepared this package version must approve it.'; B's Certify -> Certified, event platform.package_certified.v1 |
| Send back a reviewed version | Approver; Reviewed package | Send back with reason | Back to Draft; reviewer cleared; reason saved; a Draft package cannot be sent back |
| Retire blocked while in use | Certified package in an Approved or Active release; configuration owner | Retire | Refused 'Release REL-0001 still runs this version.'; if it is the only reviewed version and another reviewed package depends on it -> '<CODE> <ver> depends on <code> and no other reviewed version would remain.' |
| Versions and use tab | Package with 2 versions, a dependant and a release | Open Versions and use | Versions (newest first), Needed by (packages listing this code), Releases carrying it; each row opens its record |
Capabilities / Apps
| Action | When | What you do | What happens |
|---|---|---|---|
| App install asks the registry | Flag platform.enforce_registry off; package FIN registered but only Draft | As a company admin install or upgrade Finance from the apps screen | Refused before anything is written: ''finance' needs package FIN, which has no reviewed or certified version in the package registry.'; with FIN Reviewed it installs (PLT-A1) |
Extension record
| Action | When | What you do | What happens |
|---|---|---|---|
| Register an extension | Operator | New: namespace x_acme, hook partner.after_save, core 1, function custom_modules.acme.hooks:on_save, field 'contacts.partner x_acme_tier'; Save | Draft created; name defaults to x_acme; written for hook v1; Checks tab lists anything missing |
| Review and activate by someone else | Registered by A (operator); B release manager; C approver | B Review; A tries Activate; C Activate | Review needs no open problems (else first problem shown); A refused 'Somebody other than the person who prepared this extension must approve it.'; C activates only if the function imports, else 'The function cannot be loaded: ...' |
| Disable and back to draft | Active extension | Operator Disable; configuration owner Back to draft; edit; Review again | Disabled (data stays readable); Back to draft allows edits; Active -> edit not offered |
Release record
| Action | When | What you do | What happens |
|---|---|---|---|
| Create a release | Operator; an Approved environment STAGING; certified packages | Releases > New: Title, Environment STAGING; Save; Packages tab: Edit, tick 2 packages, Save packages | Number REL-0001 (next free); state Draft; migration head defaults to this build's heads; the only active policy that fits the tier is attached automatically; Packages tab lists both with Digest matching |
| Only reviewed/certified versions offered | Draft package exists | Release Packages tab in edit | Draft/retired versions not listed ('Only reviewed or certified versions can go into a release.') |
| Attach an SBOM | Draft release | Evidence tab: Take and attach an SBOM | SBOM digest, component count and 'no blocking advisory' / 'n blocking' shown; only in Draft (else 'An SBOM is attached while the release is a draft.') |
| Submit gate: draft -> reviewed | Draft release with rollback plan, SBOM (if the policy asks), licences approved | Gates tab: read 'Before submit'; press Submit | Checks licence, artifact digests, dependencies, core and vulnerabilities (+ SBOM when the policy lists sbom). All pass -> Reviewed; artifact hash and provenance (commit, clean tree, host, time) written; gates recorded with time |
| Submit refusals | Draft release | Submit with: empty rollback plan; then empty migration head; then a package whose licence is Pending; then no SBOM and a vulnerability gate | 'Write the rollback plan: forward fix or restore, and how.'; 'Say which migration head the release brings the schema to.'; 'Every package's licence is approved: Licence review open for X_DEMO 1.0.0 (pending)'; 'No unwaived high or critical advisory: No SBOM: nothing to check vulnerabilities against.' - state stays Draft but the failing gates are saved on the Gates tab |
| Drifted artifact blocks submit | A package in the release drifted after it was added | Submit | Refused 'Package artifacts match their recorded digest: X 1.0.0: the files no longer match the reviewed digest.' (PLT T008) |
| Unwaived high advisory blocks submit | SBOM contains a component matched by a high/critical advisory | Submit; then waive the advisory; Submit again | First: '... 1 unwaived high or critical advisory(ies) in the SBOM.'; after waiver it passes (advisories are re-checked at the gate) |
| Upgrade rehearsal | Draft or Reviewed release; a stored backup | Evidence: choose Newest backup; Rehearse the upgrade on a restored copy; then Job runs > Run the scheduler now | A rehearsal drill DRL-xxxxx is queued as a run of platform.upgrade_rehearsal; after it runs it shows Passed with 'Reached' = the release's migration head, or Failed with the reason |
| Stage gate: reviewed -> staged | Reviewed release; release manager | Stage | Needs a PASSED rehearsal of THIS release in the last 30 days reaching its migration head (+ restore drill / module contracts when the policy lists them). Without -> 'Upgrade rehearsal passed on a copy: No passed upgrade rehearsal for this release in the last 30 days.'; wrong head -> 'Rehearsal DRL-00002 reached <x>, not <y>.'; with policy restore and none -> 'No passed restore drill in the last 30 days.' |
| Approve with one approver | Staged release, policy approvers 1; user C approver who did not create, submit or stage it | Approve: QA disposition, tick 'I have read and accept the rollback plan'; OK | State Approved; approver C; manifest written (lines, artifact hash, migration head, rollback plan digest, QA, approvers, SBOM, provenance, core 1.0.0) and HMAC signature shown 'valid'; Approvals 1/1 |
| Approve with two approvers | Policy approvers = 2; approvers C and D | C approves; check state; C approves again; D approves | After C: still Staged, Approvals 1/2, history 'release.approval_recorded'; C again -> 'You have already approved this release.'; after D: Approved and signed with approvers [C, D] |
| Activate gate | Approved release; deployment already done on the server | Activate | Checks: signature intact, artifact hash unchanged, digests, verified backup inside the environment RPO, inside the maintenance window, live schema = migration head. All pass -> Active, environment's Active release = this one, environment migration head updated, event platform.release_activated.v1 |
| Activate: backup older than the RPO | Environment RPO 1440 min; newest verified backup finished 1500 min ago | Activate | Refused 'A verified backup inside the RPO: The newest verified backup is 1500 min old; the RPO is 1440 min.'; with a backup 600 min old: 'Verified backup 600 min old (RPO 1440 min).' passes; no verified backup -> 'No verified backup. Take and verify one first.' |
| Activate: maintenance window arithmetic | Environment window Friday 23:00, 120 min, Asia/Dubai | Try Activate on Fri 22:50 Dubai, Fri 23:10, Sat 00:30, Sat 01:05 | 22:50 refused 'Outside the maintenance window (Friday 23:00, 120 min, Asia/Dubai).'; 23:10 and Sat 00:30 inside (window opened Fri 23:00 and lasts to Sat 01:00); 01:05 outside. With no start time: 'No maintenance window is set: any time is allowed.' |
| Activate: schema not migrated | Release migration head differs from the live database | Activate | Refused 'Live schema is at <live>; the release expects <head>. Deploy and migrate first.' |
| Activation supersedes the previous release | REL-0001 Active on STAGING; REL-0002 Approved for STAGING | Activate REL-0002 | REL-0002 Active; REL-0001 becomes Retired with reason 'Superseded by REL-0002.'; environment Active release = REL-0002 |
| Manifest tampered after approval | Approved release whose stored manifest is changed (test DB only) | Activate | Refused 'The approved manifest no longer matches its signature. It was changed after approval; reject the release and approve it again.'; changed lines -> 'The release's packages are not the ones that were approved.' |
| Recover: forward fix | Active REL-0002; another release REL-0003 for the same environment | Recover: Outcome Forward fix, fixing release id = REL-0003's internal id, reason | State Recovered, forward fix linked; another environment's release -> 'Name the fixing release for the same environment.' |
| Recover: restored from backup / failed | Active release; a passed restore drill | Recover: Restored from backup with the drill id; on another release Recover: Failed - not recovered | Restored: state Recovered with the drill linked (a failed drill -> 'A restore is evidenced by a passed restore of that backup.'); Failed outcome: state Failed. No down-migration is run |
| Send back to draft | Reviewed or Staged release; approver | Send back with reason | Back to Draft; staged by and approver cleared; any recorded approvals deleted; reason shown in Last reason; history kept |
| Cancel | Draft/Reviewed/Staged/Approved release; release manager | Cancel with reason | Cancelled; no further steps offered; an Active release cannot be cancelled ('A active release cannot be canceled.') |
| Retire | Active or Recovered release; configuration owner | Retire with reason | Retired; if it was the environment's active release the environment has no active release |
| Edit only while draft | Reviewed release | Try Edit (API PUT) | 'Only a draft release changes. Reject it back to draft first.' |
| Idempotency key | Draft release (API) | POST submit with idempotency_key K twice; then K with a different body | Second call returns the first answer with replayed=true and nothing happens twice; different body -> 409 'That idempotency key was already used for a different request.' |
Environment record
| Action | When | What you do | What happens |
|---|---|---|---|
| Create an environment | Configuration owner | New: STAGING, Staging, RPO 1440, RTO 240, window Friday 23:00 120 min, secret env:A2N_DB_PASSWORD; Save | State In review; smart 'Window Open/Closed' follows the time; only one environment can be 'This server' |
| Approve by someone else, then deploy | Created by A; B approver; operator | A tries Approve; B Approve with reason; operator Mark deployed | A refused 'Somebody other than the person who prepared this environment must approve it.'; B -> Approved and a new row in Approved versions (#1, snapshot tier, RPO, RTO); operator -> Deployed |
| Change an approved environment | Approved or Deployed environment | Try Edit; press Back to review; Edit RTO; Save | Edit not offered until Back to review ('Send the environment back to review before changing it.'); then saves; history lists before/after |
| Archive refused while a release is in flight | Staged/Approved/Active release on the environment | Archive | Refused 'Release REL-0001 is still in flight here.'; archived environments disappear from the release Environment picker |
Scheduled jobs
| Action | When | What you do | What happens |
|---|---|---|---|
| Platform jobs created on first visit | Fresh workspace | Open Scheduled jobs | 7 jobs: platform.metrics_snapshot (every 5 min, Enabled), platform.alert_scan (5 min, Enabled), platform.package_scan (daily 03:00, Enabled), platform.sbom (weekly Sunday 03:30, Tested), platform.prune (daily 04:00, Enabled), platform.restore_drill and platform.upgrade_rehearsal (On demand, Tested, 1 attempt) |
Job record
| Action | When | What you do | What happens |
|---|---|---|---|
| Create, test and enable a job | Configuration owner + operator | New: code test.heartbeat, Handler Heartbeat, Every N minutes 60; Save; Run now (blank key); Run the scheduler now; Enable | Created Draft; Run now on a Draft runs as a test (trigger 'test'); when it succeeds the job becomes Tested automatically; Enable -> Enabled with Next due set; a Draft job cannot be enabled directly ('A draft job cannot be enabled.') |
| Interval slot arithmetic | Enabled job every 60 min | Enable at 10:17 Dubai (06:17 UTC); read Next due | Next due 11:00 Dubai (07:00 UTC): interval slots are counted from 1 Jan 2026 00:00 UTC, so a 60-minute job runs on the UTC hour; run key '2026-10-02T07:00Z' |
| Daily schedule in Dubai time | Daily job At 02:00, Asia/Dubai | Enable on 2 Oct after 02:00 Dubai | Next due 3 Oct 02:00 Dubai = 2 Oct 22:00 UTC; run key '2026-10-02T22:00Z'; weekly uses the chosen weekday (Monday 0 ... Sunday 6) |
| Missed slots after downtime | 60-minute job; scheduler off for 3 hours | Turn the scheduler back on / Run the scheduler now | Only the latest due slot is accepted (one run), not three; Next due moves to the next future slot |
| Same run key is one run | Enabled job | Run now with run key 'test-key-1' twice | Second press returns the same run (created = false); Runs accepted +1 only; the job's Run now is hidden while Paused ('Enable the job first (a draft job runs only as a test).' via API) |
| Pause and back to draft | Enabled job | Pause; Back to draft; edit; save | Paused clears Next due and is not claimed; Back to draft (configuration owner) allows edits; runs already accepted keep the policy they started with (Policy at acceptance on the run) |
Run record
| Action | When | What you do | What happens |
|---|---|---|---|
| Retry with backoff then dead letter | Job with Attempts 3, Backoff 60 s whose handler fails | Run it; run the scheduler repeatedly | Attempt 1 fails -> Waiting to retry, next retry +60 s; attempt 2 -> +120 s; attempt 3 fails -> Dead letter, event platform.job_failed.v1, error code and redacted message shown; with Backoff 3600 the 4th wait would be 28800 s but is capped at 21600 s (6 h) |
| Retry a dead letter | A Dead run | Open it; Retry (same run key) | Back to Queued, attempts 0, same run key; side effects already recorded are not repeated; a Succeeded run shows no Retry ('Only a dead or cancelled run is retried.') |
| Cancel a run | A Queued run and a Running run | Cancel each | Queued: Cancelled at once with error 'Cancelled on request.'; Running: cancel requested, it stops at its next check and ends Cancelled; a finished run -> 'This run has already finished.' |
Job health
| Action | When | What you do | What happens |
|---|---|---|---|
| Reconciliation | Several runs in different states | Open Reporting > Job health | For each job Accepted = Queued + Retrying + Running + Succeeded + Dead + Cancelled and Reconciles = Yes; the line says 'Reconciles' |
Restore drills
| Action | When | What you do | What happens |
|---|---|---|---|
| Run a restore drill | Operator; a stored backup with fingerprint | Run a restore drill: Newest stored backup; OK; Job runs > Run the scheduler now; open the drill | DRL-00001 queued then Passed: 'Restored n rows in m tables; every count, file digest and ledger total matches.'; Rows n/n, Stored files match, Ledger totals match; RTO measured; RPO = backup age in minutes; the scratch copy is removed |
Drill record
| Action | When | What you do | What happens |
|---|---|---|---|
| Drill against RTO/RPO targets | Drill linked to an environment with RTO 240 min, RPO 1440 min (via rehearsal or API environment_id) | Run with a backup 1500 min old | Failed: 'The backup was 1500 min old, beyond the RPO of 1440 min.'; a restore over 14400 s -> 'The restore took <n>s, beyond the RTO of 240 min.' |
| Old backup without fingerprint | A backup taken before fingerprints | Run a drill on it | Only row counts reconciled; message ends 'This backup was taken before file and ledger fingerprints were recorded, so only row counts were reconciled.'; files/ledger show 'not fingerprinted' |
| Corrupt backup file | A stored backup whose file was changed (test only) | Run a drill | Failed 'The stored backup is not the file that was written: its checksum differs.'; differences listed where counts differ |
Recovery evidence
| Action | When | What you do | What happens |
|---|---|---|---|
| Recovery report | Several finished drills | Open Reporting > Recovery evidence | Line 'n / m drills passed - x with every row count matching - y with file digests matching - z with ledger totals matching'; green 'Reconciles' only when every finished drill passed |
Alerts
| Action | When | What you do | What happens |
|---|---|---|---|
| Shipped alert policies | Fresh workspace | Open Alert policies | 6 active policies: QUEUE-LAG (>900 s, 15 min, critical), P95 (>2000 ms, warning), ERRORS (>5 %, warning), DEAD-JOBS (>0, warning), BACKUP-AGE (>26 h, critical), DRILLS (>0 failed in 30 days, warning, release manager) |
| Alert opens and auto-resolves | A dead-letter run exists | Alerts > Evaluate now; then retry the run until it succeeds; Evaluate now | DEAD-JOBS opens one alert: detail 'Dead-letter jobs is 1.0 (> 0.0) over 60 min.' with up to 5 correlation ids; a second evaluation updates the same alert (no duplicate); when the count is back to 0 the alert becomes Resolved |
| Queue lag arithmetic | Oldest queued run waiting 20 minutes (scheduler off) | Evaluate now | QUEUE-LAG value 1200 (> 900) opens a Critical alert; lag above a week is reported as 604800 |
| Acknowledge | An open alert; operator | Press Acknowledge on the row | State Acknowledged; button disappears; still counted in Overview Open alerts until resolved |
Alert policy record
| Action | When | What you do | What happens |
|---|---|---|---|
| Create, change and pause a policy | Configuration owner | New policy; Activate; try Edit while active; Pause (operator); Edit; Activate | Edit refused while active ('Pause the policy before changing it.'); paused policies are not evaluated |
Flag record
| Action | When | What you do | What happens |
|---|---|---|---|
| System flags | Fresh workspace | Open Feature flags | platform.enforce_registry (workspace) and platform.uninstalled_apps_readable (per company) exist as Draft, value false |
| Propose, approve, version | A config owner, B approver | A creates sales.quick_quote (Yes / no); Propose a value true; B Approve | After propose: Proposed, Waiting true, In force false; after B: Active, value true, Version 1 row with approved by B; event platform.flag_changed.v1 |
| Change an active flag | Active flag value true | A: Propose a change -> false; B: Approve the change | Old value stays in force while waiting; after approval value false, Version 2 |
| Refuse a proposal | Proposed flag | B: Refuse with reason | Back to Draft, pending cleared, reason kept |
| Effective dates | Active flag true with Effective to = 5 minutes from now | Wait past the end; reopen | 'In force' reads false (the default) after the end, with no new approval |
| Per-company value | Active per-company flag | Companies tab: choose a company, value true, Set for this company; Remove | Row added and that company reads true; Remove deletes it and the company falls back to the flag value |
| Uninstalled apps readable | platform.uninstalled_apps_readable approved true for one company; an app turned off there | Open a list of the turned-off app; try to save a record | Reads answer; every change refused. Other companies (flag false) are refused everything as before |
Bills of materials
| Action | When | What you do | What happens |
|---|---|---|---|
| Take an SBOM and download | Operator | Take an SBOM now; open it; Download CycloneDX | Components from Python and npm with licences; Licence flags for unknown/copyleft licences; file sbom-<digest12>.cdx.json downloads |
Vulnerability advisories
| Action | When | What you do | What happens |
|---|---|---|---|
| Import and waive | Release manager imports, approver waives | Import the sample record (requests <9.0 high); Waive with reason and end date; Lift waiver | Created 1 (a second import updates it); the SBOM shows a blocking finding; waiver shows 'until <date>'; Lift returns it to blocking |
Platform roles
| Action | When | What you do | What happens |
|---|---|---|---|
| Grant and revoke | Workspace owner | Grant a role: test.operator / Operator; Revoke | Row Active Yes, Granted by owner; Revoke -> 'Revoked' (row kept); revoking again -> 'That role is already revoked.' |
Platform audit trail
| Action | When | What you do | What happens |
|---|---|---|---|
| Every command audited | Perform a release submit | Open Audit trail; filter Record = release | Row with who, action release.submit and correlation id; the same correlation id is on the response header X-Request-ID |
Capabilities
| Action | When | What you do | What happens |
|---|---|---|---|
| Company capability list | A member with no platform role | Open Platform (or Reporting > Capabilities) | Note about the console; list of the company's installed apps with reviewed package version and Certified / Reviewed / Not reviewed; server tier and 'Core 1.0.0'; no host names or secrets |
Workload
| Action | When | What you do | What happens |
|---|---|---|---|
| Latency measurements | Use the app for a few minutes | Operations > Workload; Measure again | Per route p50/p95/p99 and 5xx counts; values are for this server process only and reset on restart |
Record screens
| Action | When | What you do | What happens |
|---|---|---|---|
| Edit conflict (revision) | Same release open in two tabs | Tab 1 Save; tab 2 Save or Submit | Tab 2 refused 'Somebody changed this record since you opened it. Reload and try again.' (409); nothing overwritten |