Appendix — every change in this release
Every change between the last alpha build and GA — 248 commits,
grouped by kind. The Bug fixes section above is a selection; this is the
complete record. Generated from v1.3.0-09..69e614a; the commit that writes
this appendix is necessarily not listed in it.
Features (60)
a692e82feat(alerts): watch data caps, and fix an alert that could never fire (#35)db09e49feat(health): qualify "Connected" with how often it has dropped (#28 part 2)8766f0cfeat(support): cumulative health counters — a flap was invisible (#28)6547841feat(support): say whether the box is licensed, and why the cap disagrees159900efeat(events): AAA faults in the alert centrea7d3d75feat(controller): bound the retained config history (bng#41)6e66f4cfeat(support): collect intent, not just running state; fix the capture1ddc2cbfeat(support): consolidate the bundle and lead with a written summary6c998affeat(support): one-button diagnostic bundle for a support ticketd27b574feat(aaa): subscriber_id is an exact key everywhere (#25)dd2ad55feat(rates): A-06 in the subscriber drawer, with the cap consequence stateddbf33ecfeat(rates): A-06 — a raw per-subscriber rate, expressed as a plana93def0feat(caps): wire the per-subscriber surfaces an operator could not reach6520bf5feat(billing): customer_ref reaches exported CDRs — closes the G-11 gapf350e5dfeat(caps): data-cap panel on the subscriber drawere938fa5feat(caps): Data Caps page and its client51768fafeat(caps): the operator API for data-cap policyd490741feat(caps): run the evaluator on the drain tick, publish bngcap*4c8c146feat(aaa): converge enforcement overlays every reconcile pass52f51d9feat(caps): declare the data-cap webhook events at registratione12bee1feat(caps): evaluate_once -- usage in, enforcement rows out5c33019feat(caps): resolution and decision, as pure functions92c7914feat(caps): sum usage over a cap cycle windowc807ca0feat(caps): CapPolicyStore -- allowances, overrides, grants, cycle state442dd65feat(caps): the cap cycle calculatora2f89b5feat(aaa): surface unmanaged subscribers with adopt and removefa36791feat(aaa): run the reconciler and publish what it does2800a25feat(aaa): write durably before pushing to the controller9dacdcdfeat(aaa): detect a wiped controller and restore the base5a09424feat(aaa): converge the controller onto our store8c1ba55feat(aaa): one-time adoption of the controller's subscriber base5268a80feat(aaa): durable subscriber storea86eecffeat(accounting): the page becomes a view over the ledger303fc76feat(accounting): publish drain lag, age and errorsb2673e8feat(ledger): daily prune and scheduled exportcf5ca6afeat(accounting): read and export the ledger3c3544dfeat(ledger): 13-month retention and historised identityc6cf03efeat(accounting): drain the spool server-side, ack only after commit5e77571feat(ledger): daily usage by differencing cumulative countersdd94744feat(ledger): durable accounting store7e5c82bfeat(about): stamp the wheel with the commit it was built from1290760feat(subscribers): show an account's sessions, and let an operator end them6be6c4dfeat(static-ip): assign an address from the subscriber drawer39829b6feat(static-ip): reject a conflicting address before it is written02aa0ebfeat(static-ip): CSV columns, and rename the observed address1654edefeat(static-ip): reject an address the pool will later lease0caca48feat(capacity): make a full box impossible to missda92855feat(add-ons): close the update-checking half of the lifecycle90312bbfeat(marketplace): brighter selector, and Check now shows its work92f2797feat(marketplace): restore the page selector between Licensing and Add-ons605f411feat(bundle): pin upstream v1.4.0 and core 0.10.1782cc22cfeat(webui): two AAA controls, and delete the inert recovery-age knob079b383feat(aaa): locators, subscriber_id keying, and the new bulk shapee1e70d0feat(aaa): source selector replaces the removed mode fieldac535aafeat(webui): Licensing leads with the fingerprint and the cap in force86d43d0feat(webui): Marketplace section with the Add-ons pagec6a45affeat(addons): update checking that knows when to stay quiet5b2e3f2feat(addons): both modules install from an uploaded tarball8086199feat(addons): the catalogue, with a URL an offline mirror can owne313512feat(addons): retain one previous artifact, and roll back to it offline
Fixes (91)
66c8283fix(sessions): an unavailable session list must not render as an empty oned05ed72fix(webui): drop two false claims from the topology, and stop the session drawer opening blank5f31de2fix(statseg): the sizing axis is threads, not subscribers6a47925fix(install): the statseg fix wrote a key core never reads — bundle -10 is a no-opccc0c56fix(docs): the stats segment IS measurable — I checked the wrong route21c5c10fix(install): size VPP's stats segment — nothing in the product did (#42)72862b3fix(health): two red RADIUS cards on every healthy 1.4.0 box were a false alarm (#39)00d05dffix(webui): the Topology page had no AAA element at all, and Pools read DNS from a key that does not exist (#39, #40)c2e9128fix(restore): verify an access-port write landed instead of trusting 200 (#30)1389fedfix(sessions): drop the Subscriber column for 1.4.0; restore it in 1.5 (#27)c180ff6fix(licensing): readgrants, not a field the controller never sent (#32)4936c90fix(snapshots): a restore would have disabled every access port (#30)b2d734cfix(webui): the cap caption said "Community" on a licensed box991ab53fix(sessions): draw the cap line at the cap actually in forcec1c9ed3fix(licensing): activation works from the consolef116a40fix(licensing): a trailing newline was invalidating valid licences8259ab9fix(snapshots): make restore actually restore4f6d033fix(captive-portal): stop the portal_ip save erasing the walled gardenaf961cffix(support): run the collection detached — an in-worker thread killed the dashboarde0f8175fix(support): parse BUNDLE_VERSION; bundle 1.4.0-057de3579fix(webui): an unprovisioned subscriber_id is a write key, not a join key (#25)3ad4709fix(tests): stop the suite running production daemons (#21)cb2468ffix(aaa): deleting a capped subscriber no longer strands its overlay012e639fix(controller): importing as non-root must not die on the CA probe348244efix(aaa): leave unmanaged subscribers' enforcement overlays alonec2addbcfix(webui): data-caps client uses fetchWithAuth; input bounds match server6c8b8f6fix(aaa): make post-reprovision overlay replay non-fatal6fa4654fix(caps): hoist status() queries out of the per-subscriber loope7fa291fix(caps): add grants_in_window; drop two dead helpersb4f01bafix(caps): disabling enforcement now clears every overlay5eb75a0fix(caps): stop cross-subscriber leakage in the grant panel0434621fix(caps): surface write failures, drop grant form, protect settings draftfc1a2adfix(caps): fix round 1 -- connection leak, coercion 500s, test gaps7947075fix(aaa): guard enforcement convergence against non-controller failuresc87dac5fix(caps): test coverage and boolean logic for NULL handling1524590fix(tests): stop the suite running a live metrics sampler2bdae92fix(api-keys): survive, and repair, a keys file still in the bare-list shape9ca5d80fix(proxy): close the third door into the AAA subscriber basede1c146fix(aaa): drop a subscriber from the unmanaged list once it becomes oursd52c944fix(aaa): restore the merge base for a record we do not hold52f9e41fix(webui): the wipe notice must name aaa_source among what was not restoredb415e3cfix(aaa): debounce the wipe guard, tell its conditions apart, and correct §5eca818efix(aaa): close the two proxy routes that write only the controllerb2b5c08fix(aaa): merge over the record, not over the controller06475c5fix(aaa): stop an orphan annotation poisoning the whole restore46e073dfix(aaa): say the switch may need re-registering before the plans678e419fix(aaa): stop a refused restore wedging the reconciler, and say whya4d6665fix(aaa): let the circuit decide which line a mint may reuseb72441bfix(aaa): roll back a rejected apply, and stop one bad row killing reconcilec39b7e3fix(aaa): record a bulk import before pushing it, closing the seta5aff25fix(aaa): close the delete and mint holes the inversion opened169b7a2fix(aaa): surface reactivation_history through get_subscriber3ccbb2cfix(ledger): three residual ways the daily maintenance could destroy billing data31c7730fix(accounting): a quarantined billing record must not be invisible2bc628cfix(accounting): a retained live population must not starve the drain forever8e33280fix(ledger): export what is about to be pruned, not the whole ledger daily41f7a9bfix(accounting): filter CDRs in SQL so an old date range is reachable9afd934fix(ledger): refuse an implausible retention_days instead of deleting a yearebcc8c3fix(accounting): distinguish a failed CDR fetch from an empty ledger4b8a749fix(accounting): count a raised drain cycle as an error toof0bf65ffix(ledger): drain thread's sqlite connection was unusable cross-thread863b36efix(accounting): page forward within a cycle instead of re-fetching a static window53bede3fix(accounting): review fix wave -- vacuous tests, reset heuristic, late-interim double-credit7cb61c2fix(ledger): per-record SAVEPOINT so a DB error on delete can't split a record9257da3fix(ledger): make write_records genuinely atomic8b4a2e0fix(build): only tracked changes make a build dirtyef6a848fix(static-ip): narrow the unassign sentinel to framed_ip/subnet_maskecb0da7fix(static-ip): check zero-padded addresses, and two tests that could not fail7084e34fix(subscribers): a failed session fetch is not an empty account92c0810fix(aaa): stop the raw subscriber PUT proxy erasing a static IPb4838f2fix(static-ip): tell "not mentioned" apart from "unassign me"1e42a42fix(static-ip): wire framed_ip/subnet_mask into compute_diff2d27b9bfix(webui): restore the markup two descriptions lost to a narrow typecc8aa10fix(typecheck): clear the 16-error backlog exposed by the tsconfig rootDir fix3dadea1fix(add-ons): the primary Install button never said Installing…75e652efix(add-ons): restore the expander that described what the pack contains6a4062efix: the eval appliance HAS a TPM — correcting a wrong premise9b694effix: two error states that lied, and the CPESIM pind52be84fix(eval-appliance): find the bundle our own build script produces5b1d434fix(aaa): merge subscriber PUT over stored record, not just match6061b2efix(1.4.0): whole-branch defects — locator erasure, bulk crash, false alertfa7e334fix(webui): guard health.radius reads against the 1.4.0 shapecd3e05efix(health): stop fabricating a radius block in degraded fallbacks2958feefix(portal): voucher redemption can finally activate a session35e8775fix(aaa): carry locator columns through the upload pipeline, spec-validate the two rewritten bulk testsfa03fd3fix(aaa): rewrite the two route-level tests Task 3s rewrite broke9f75262fix(aaa): delete the cascade planner that never fired131f376fix(deps): declare jsonschema/referencing as dev extras0568042fix(webui): API Keys page rendered blank on every box; replace two screenshots6a8b3f8fix(appliance): clear intermittent first-boot hang with rcu_normal; ship 1.3.0-13d89225ffix(licensing): the three controller paths were missing /v1 and 404d
Performance (2)
c29a377perf(metrics): stop shelling out every 5s for counters we discard (#36)b8c4d3cperf(metrics): stop fetching the whole controller config every tick (#14)
Documentation (57)
84ae8d6docs: point the dataplane known issue at its real issue number (#49)7aeebbadocs: record the dataplane port-assignment issue and its workaround9a12835docs(release): regenerate the appendix against 433b916 (243 commits)433b916docs: roll the 1.4.0 doc set to bundle 1.4.0-13 / plugin 0.10.3675a7bccdocs(release): frame 1.4.0 as GA, with everything prior as one alpha bucket897c4bbdocs(release): append the complete commit record9e7b755docs(release): the commit count was stale — 229, not 20451ed4d4docs(release): carry the controller and VPP side of 1.4.0 (#43)7d3dd4fdocs(release): remove internal framing and the "shim" terminology84c7f4bdocs(release): bundle 1.4.0-10, and say what the installer now does about statsegefee8aadocs(aaa): correct a comment I generalised from one configuration236b0eedocs(release): say what the console actually shows for RADIUS and AAAc7ace1edocs(release): 1.4.0 notes — known issues now cover the open set, and Upgrading no longer over-promisesd586affdocs(metrics): document all 36 families, and guard against the drift (#34)8f1e614docs(lifecycle): mark §4 and §9.3 superseded by the state-envelope restore10eb38adocs(spec): record the #24 amendmentsa998128docs(spec): §5.2's enabled_ts floor is day-granular — state the limit2ec2311docs(plan): sub-project E implementation plandcb9614docs(spec): sub-project E — data-cap enforcement0e5682adocs(plan): AAA as system of record (sub-project F)d7eb45cdocs(spec): AAA as system of record (sub-project F)96aeea3docs(lifecycle): QuantumTouch takes ownership of AAA8e1f111docs: state ownership and lifecycle — what the controller must persist8a6be39docs(spec): the binding spec described behaviour that was deliberately deletedb186183docs(plan): accounting ledger (sub-project D)1aab859docs(spec): accounting ledger (sub-project D)3ff8da5docs(plan): sub-project C — static IP and subscriber sessionsa939894docs(spec): static IP and subscriber sessions (sub-project C)bc4c816docs: preserve the 1.4.0 port execution ledger; ignore vagrant state7c96972docs(artifacts): the _test endpoints are NOT served in production — correctiond6b4bafdocs(artifacts): correct the health.radius claim — twice wrong366f931docs(plan): implementation plan for the controller 1.4.0 portc818b2edocs(spec): the cascade has never fired, and sessions do carry subscriber_id2504d03docs(spec): controller 1.4.0 port — design for sub-project Ae109e42docs(artifacts): import the 1.4.0 controller API spec and evaluate it against our tree50c3bdbdocs: direct upload for disconnected boxes; URL path is the orchestrator seam5163235docs: rename About to System and reserve it for platform lifecycle12aeee9docs: correct -- capacity licences are perpetual, not subscriptionsca3fdbedocs: draw the licence expiry cliff, and free amber for it2bea687docs: record the three capacity bands, and two consequences of replace978203bdocs: scope the design to QuantumTouch; hand the store findings off99699d5docs: session cap replaces rather than stacks845b417docs: nav group is Marketplace, holding Licensing and Add-ons9e81489docs: CPESIM need not stay pinned -- the blocker is an unversioned engine APIb7a1e27docs: read-only operator gets an "ask an administrator" fingerprint card536dee1docs: machine fingerprint leads the Licensing pagefc25baddocs: speed caps replace rather than stack; interface config is a prerequisite16bcc14docs: design for the add-ons + entitlements console section3bbe2f4docs: publishing instructions for the 1.3.0 docs web implementerb627fa5docs: state survey ahead of lifecycle managementd248b24docs(grafana): customer guide for the dashboard pack; pack is free and unlicensedbcbaf0cdocs: capacity licensing is staged for 1.4.0, not a 1.3.0 capability19b9f6ddocs: correct the licensing documentation — the console flow does not work2e437fcdocs: response to the feature-query interface proposal (#33)7b9796adocs: re-shoot the console screenshots, and fix two things they caught68cbb67docs: update the operator guides for 1.3.03027512docs: review the 19 published QuantumBNG docs against 1.3.0
Tests (14)
56f8baatest(radius): tag lab-dependent tests so a release run can drop themd4707f4test(aaa): cover the branches that had none, and verify each catches its regressiona10af89test(aaa): prove the reconciler is actually reachable from the drain threadc0b397atest(aaa): pin remove_unmanaged's unreachable-controller orderingdd5741atest(accounting): assert the test drain thread does not outlive its teste4c55adtest(ledger): pin the INSERT OR IGNORE rule at the statement itselfd650317test(accounting): pin the real lag computation, and ship spec 8's second alert3225513test(subscribers): cover the session client directly, not through the drawer12140d1test(subscribers): replace a vacuous role="alert" check with a real one17b91b7test(static-ip): pin the conflict/generic-failure routing to the error code6fadfa6test(health): update happy-path fixture to the 1.4.0 aaa shapeabbe19btest(aaa): fix stale mode fixture, cover radius rejection0e75d2atest: a controller double that enforces the real 1.4.0 contract5457c62test(webui): repair two stale Dashboard tests and the pool card router context
Build and chores (7)
fe8dc75chore(deps): lock @quantumtouch/core to the v0.10.23 commitda25223chore(deps): core 0.10.23 for plugin webhook event registrationa6ea37echore(deps): lock @quantumtouch/core to the v0.10.22 commite63c9d9chore(deps): core 0.10.22, and a guard so the two pins cannot drift4cbcab9chore(deps): quantumtouch-core 0.10.21 — accessible Field labelsab40ce3chore(bundle): cut 1.4.0-0254acdd4chore: ignore .worktrees/
Release (9)
69e614arelease: bundle 1.4.0-14, plugin 0.10.37, CPESIM 0.4.18a47b639release: bundle 1.4.0-13, plugin 0.10.36a79fa2arelease: cut installer bundle 1.4.0-1204ed60arelease: bundle 1.4.0-1144a426arelease: plugin 0.10.34 — the version never moved across 1.4.02caaaf9release: bundle 1.4.0-10d4807b8release: record the bundle 1.4.0-09 version bump that shipped to the rig1fcf348release: bundle 1.4.0-048673133release: bundle 1.4.0-03
Other (8)
915dc44Merge release/1.4.0-13: sessions fetch-failure fix + 1.4.0 doc setc5db7c5Revert the Pools DNS change: #40 was not a bug, and my test disproved itbe425ddMerge support-bundle health counters, snapshot access-port fix, cap captioncd0f367Merge A-06: per-subscriber raw rate overrideae00fbfMerge #24 follow-ups: billing interop, wired cap surfaces, overlay ownershipd39660cMerge sub-project E: data-cap enforcement0f0fc5fRestore installedLinks, capability reload, and backstop poller on Add-onscc432d3Merge feat/controller-1.4.0-port: port to controller 1.4.0