Release planning

CadCraft 1.0 impact map

Every fix and feature lined up for 1.0, and what each one touches: which apps, and which layers of the stack.

ProjectCadCraft
Sheet0.10 impact
Items110
Date2026-11-09
51Fix
22Feature
0Spike
28Platform
0Site ops
8Sanity check
1Decision
≈ 319days in the written 0.10 plans
  • Planned 101 on the plan for the release it is filed under
  • Built 0 built and working in /lab (the dev builds). Whether the public has it is its reach: "in the lab", "Live" (the last gate) or "switched off"; see docs/REACH-LIVE.md
  • Partly built 1 part of it is built in /lab; the rest waits (see "Waits on")
  • Promised 0 the live site already says it is coming
  • Moved 0 moved here from an earlier release or wishlist
  • Found 0 bug found after a release shipped; fixed in a point release or the next one
  • Candidate 3 recommended in a spike or session log; owner to choose
  • Community please test 0 built and in /use; a Discord post gives the steps and the expected result and asks people to try it on their own devices, files or know-how
  • Decision Time 5 a decision, check, file, key, flag or go only you can give; folds into the list once answered

Built means built and working in the lab. Live means the public can use it, and is the last gate: 0 Built · Live, 0 Built · in the lab.

Sheet 1 · impact areas

Where 0.10 lands

How many items touch each app and each layer, by kind. One item can touch several areas, so the rows add up to more than the item count. Pick a row to filter the schedule below.

Impact areaFixFeatureSpikePlatformSite opsSanity checkDecisionAll
Apps
CadCraft 20 17 – 10 – 3 1 51
BimCraft 18 20 – 9 – 4 1 52
ModelCraft 19 20 – 10 – 3 1 53
MarkupCraft 12 16 – 9 – 3 1 41
TakeoffCraft 14 16 – 9 – 3 1 43
CivilCraft 21 18 – 10 – 4 1 54
DetailCraft 15 17 – 9 – 4 1 46
PartCraft 15 17 – 9 – 3 1 45
Studio – 1 – – – – – 1
SuperCad Studio – 6 – 6 – 1 1 14
Layers
Site and services 13 3 – 8 – 1 – 25
Desktop host – 1 – 1 – – – 2
App shell 5 15 – 8 – 2 1 31
App code 23 4 – 3 – – – 30
Our engines 8 – – 1 – 1 – 10
Vendor engines – – – – – – – 0
Drawing surface 3 – – – – – – 3
Help and words 2 1 – – – 1 – 4
Tests and tooling 2 1 – 2 – 3 – 8

Sheet 2 · schedule

Every fix and feature

Grouped by track. App chips carry each app’s layer colour; Studio is in ink. "Waits on" names what has to happen first; the small lines under it say who asked and where the item is written up.

Add an item Fork map (Rust and C++ forks) The roadmap

Issues sync every 10 minutes; last sync Nov 9, 2026 7:00 pm.

ItemWhatAppsLayersStatusDaysWaits onIssues
Performance roadmap — wave 1 6
PlatformPERF.1 Link to PERF.1Comment on PERF.1 HTTP/2 for getcadcraft.com: Apache runs mpm_prefork + mod_php, so every app start is 140 to 520 requests over HTTP/1.1

Measured 2026-11-08 (tools/perf/startup.mjs, localhost): a cold /use start fetches cadcraft 398 requests / 2.6 MB (233 JS modules), bimcraft 521 / 3.8 MB, partcraft 371 / 3.2 MB, civilcraft 313, modelcraft 238, detailcraft 185, markupcraft 159, takeoffcraft 141. All over HTTP/1.1: Apache is mpm_prefork with mod_php, and mod_http2 cannot run under prefork. A browser opens 6 connections per host, so for a visitor with a 70 ms round trip the request count alone is about 400/6 x 70 ms = 4.5 s before any module runs; on localhost the same start takes 1.2 s. The fix is a server change, not code: mpm_event + php-fpm 8.3 (installed at /usr/sbin/php-fpm8.3, not in use) + Protocols h2 http/1.1. The VPS serves about 80 virtual hosts from the same Apache, every one on mod_php today, so this needs the owner's go, a maintenance window and a rollback plan (keep the prefork config). Expected: one connection, all modules multiplexed, start time near the localhost figure plus one RTT per import depth. Alternatives rejected: bundling the releases (add-ons import the apps' modules by URL; a bundle would run two copies), a CDN for app files (same request count). Decision needed: go / not now.

– Decision Time – –
PlatformPERF.2 Link to PERF.2Comment on PERF.2 Apps: the 532 KB language catalog inlined in every app page (65 % of the HTML, never cached) as a cached static file per build and locale

Measured 2026-11-08: /use/cadcraft is 828 KB of HTML (205 KB gzipped) and 532 KB of it is <script id="cc-lang"> (8,818 strings; cmd 83 KB, shell 82 KB, frame 32 KB, m3d 28 KB...), regenerated and sent on every page view because the page itself is dynamic. Readers: assets/js/app-shell.js, app-boot.js, app-lang-boot.js, i18n.js (one copy per app), supercadcraft.js, super-tools.js, exact-client.js; all read #cc-lang synchronously. Plan: Lang::bootTag writes a small tag and the catalog moves to a classic script /assets/apps/<slug>/<version>/lang/<locale>.boot.js?v= (or a generated file under a cache folder for the shell part) that sets window.ccLangCatalog, loaded with the week-long asset cache; the readers take window.ccLangCatalog first, #cc-lang second (so frozen releases and the browser edition keep working). Second visit: 0 bytes for strings instead of about 100 KB gzipped. Releases are frozen so the file can be written at release time (bin/release-app.php); a Discord member builds generate it on demand keyed by the lab stamp. Keep Lang::use (PHP needs the shell strings for the bar and Settings).

– Planned – –
PlatformPERF.3 Link to PERF.3Comment on PERF.3 Apps: the "Save for offline" file list (128 KB inline, 2,054 files stat-ed per page, 42 ms) fetched on demand instead

Measured 2026-11-08: every app page carries <script id="cc-offline"> (128 KB: the URL of every file the service worker would save) and PHP spends 14 ms in AppBuilds::offlineFiles plus 28 ms in tools/shell/offline-size.php (a filesize of all 2,054 files) to build it. It is read only when a person clicks Save for offline (assets/js/app-offline.js) and by help-panel.js for the listed-files check. Plan: keep key/page/bytes in the tag; move urls to a small endpoint (or a static JSON per release written by bin/release-app.php, a Discord member builds on demand) fetched when the button is pressed; cache offline_size per release (frozen) and per lab stamp for a Discord member. Saves about 15 KB gzipped per page view and 42 ms of PHP.

– Planned – –
PlatformPERF.4 Link to PERF.4Comment on PERF.4 Apps: modulepreload hints for the static import closure, so the import waterfall is one round trip deep instead of 4 to 8

Measured 2026-11-08 (static import graph from each release's entry module): cadcraft 281 modules, depth 6 (99 at depth 1, 104, 51, 21, 4, 1), 4.6 MB; bimcraft 422 modules, depth 4, 6.5 MB; partcraft 269 modules, depth 8; markupcraft 78 modules, depth 3. The browser only discovers level n+1 after level n arrives, so each level costs a round trip even with HTTP/2 (PERF.1). Plan: AppBuilds::headTags adds <link rel="modulepreload" href=...> for the entry's static closure (regex over import/export-from lines, cached per release in storage/ and per lab stamp for a Discord member builds; about 25 KB of tags for cadcraft, gzips well). Dynamic import() sites (38 in cadcraft, 97 in bimcraft) stay lazy. Measure with tools/perf/startup.mjs before and after; most valuable after PERF.1, still a win on HTTP/1.1 for the small deep levels.

– Planned – –
Performance roadmap — wave 2 12
PlatformPERF.10 Link to PERF.10Comment on PERF.10 Memory ceilings: 850 MB for an 8 MB DWG, 1.5 GB for a 47 MB IFC in Node; 867 to 932 MB heap peaks in the browser — a budget per format, measured in the suite

Measured 2026-11-08: Node RSS after read + write of Plans.dwg (8.2 MB) 854 MB; after open + geometry + save of IFC Schependomlaan (47 MB) 1.47 GB, the 22.5 MB showcase 1.18 GB; browser JS heap peaks: ModelCraft glTF 932 MB (10.7 MB file), BimCraft IFC 867 MB (22.5 MB), CadCraft DWG 250 MB (8.2 MB). A tab on a phone or an iPad is killed around 1 to 1.5 GB (Studio for iPad is milestone 09), and a 100 MB IFC would pass that today. Plan: tools/perf/io-bench.mjs and open-file.mjs record bytes per source MB per format at every release (PERF.6); targets: cc-dwg under 30x, cc-ifc under 20x (strings and the STEP line index kept once, not per entity), browser peak under 3x after PERF.7 to PERF.9; the apps say so before opening a file that will not fit (navigator.deviceMemory + the file's size), instead of a dead tab. Wave 3 (scale-1000x.md, apps-3d.md): cc-dwg 35 bytes of heap per file byte (76 MB → 2.6 GB), cc-ifc about 1 GB per 100 MB in the browser, ModelCraft about 2 KB per face, DXF 38 bytes per file byte, LandXML 14 times the file; point clouds 3.8 GB for 100 M points with the in-memory store. These are the per-format budget numbers.

– Planned – –
PlatformPERF.11 Link to PERF.11Comment on PERF.11 Website: /sitemap.xml takes 3.1 s and 6,042 SQL queries on every crawl

Measured 2026-11-08 (bin/admin-perf.php /sitemap): 3,118 ms, 6,042 queries (2,080 ms SQL): LibraryItems::browse x40 (one page of 100 per call, 1,293 ms: COUNT + LEFT JOIN library_versions + ORDER over 13,850 items each time), then per item LibraryVersions::byId, Auth::byId and LibraryAuthors::uuidFor (2,000 each). Crawlers fetch the sitemap all day; it is 474 KB of XML. Plan: one query for the library URLs (item slug/uuid + author uuid + current version date, joined, no per-row lookups), written to a cached file (storage/cache/sitemap.xml, rebuilt by cron hourly or on library publish) and served from there; keep the dynamic parts (news, tutorials, apps) as they are. Target under 50 ms, under 10 queries.

– Planned – –
PlatformPERF.12 Link to PERF.12Comment on PERF.12 Website: /library front page runs 83 queries (250 ms) — N+1 loads and a 51 ms stats query per visit

Measured 2026-11-08 (bin/admin-perf.php /library): 251 ms, 83 queries taking 130 ms: LibraryItems::browse x2 (55 ms: COUNT + LEFT JOIN + ORDER over 13,850 items), LibraryItems::publicStats 51 ms, AddonLibrary::all 9 ms, then Auth::byId x25, LibraryItems::byId x16, LibraryFiles::byId x15 for the cards. /library/browse and the category pages share browse(). Plan: batch the card lookups (one IN (...) per table), cache publicStats and AddonLibrary::all for 10 minutes (file cache in storage/, no APCu on this PHP), index the browse ORDER columns and make the LIKE search on 4 columns a FULLTEXT index; target under 60 ms and under 15 queries.

– Planned – –
PlatformPERF.13 Link to PERF.13Comment on PERF.13 Website: /tutorials spends 290 ms of PHP parsing every help Markdown file on every request

Measured 2026-11-08 (bin/admin-perf.php /tutorials): 292 ms, only 7 queries (2 ms); the time is Help::forApp x2 and Help::common x2 (core/classes/Help.php: glob the help folder, read and parse every .md topic, the shots map) for the index page. /tutorials/{app} and the in-app help panel pay the same per request. Plan: compile each build's help once (bin/build-help.php already exists for the compiled help.js) and cache Help::forApp/common per build stamp as JSON in storage/cache (or ship the parsed index with the build), so a request reads one file. Target under 30 ms.

– Planned – –
PlatformPERF.14 Link to PERF.14Comment on PERF.14 Lab: every /lab app page walks the a Discord member folder three times (181 ms of PHP, 2,938 files stat-ed)

Measured 2026-11-08 in-process for /lab/cadcraft: 181 ms of PHP: AppBuilds::headTags 34 ms (jsFilesDeep + filemtime of every a Discord member file for the import map's ?v=), devStamp 37 ms (path|size|mtime of every a Discord member file plus the shell), offlineFiles 22 ms + offline_size 42 ms (2,938 files), Lang 20 ms; the HTML is 1,039 KB (import map 105 KB, cc-lang 590 KB). Staff and lab testers only, and the lab deliberately never caches (Clear-Site-Data), so every reload pays it. Plan: one directory walk per request shared by headTags, devStamp and offlineFiles (a small in-request cache of [path, size, mtime]); PERF.2 and PERF.3 take the inline catalog and the offline list out of the page. Target under 60 ms.

– Planned – –
PlatformPERF.16 Link to PERF.16Comment on PERF.16 Static delivery: no Brotli (gzip only), 7-day cache on files that are already cache-busted

Measured 2026-11-08: app files come back Content-Encoding: gzip only (cadcraft.js 198 KB to 61 KB); mod_brotli is loaded but not configured, and Brotli typically takes another 15 to 20 % off JS and CSS (1.4 to 1.7 MB of modules per app start). Every ?v= file carries Cache-Control: public, max-age=604800 (7 days); frozen release files and ?v=-busted shell files never change under their URL, so they can be immutable for a year (fewer revalidations on the second visit). Plan (.htaccess + Apache conf, no code): Brotli for text/css, javascript, wasm, json, svg with gzip as the fallback (or pre-compressed .br/.gz next to release files written by bin/release-app.php, served with mod_rewrite, so compression happens once, not per request); Cache-Control immutable, max-age 1 year for /assets/apps/<slug>/<x.y>/ and ?v= URLs; keep 7 days for anything without a version in its URL. Measure with tools/perf/startup.mjs (bytes on the wire).

– Planned – –
PlatformPERF.18 Link to PERF.18Comment on PERF.18 CadCraft Studio start-up time: a startup log with timestamps in the desktop shell, measured on Windows and macOS

Not measurable on the VPS (Studio is a Windows/macOS/Linux desktop shell around the web apps; the page part is PERF.1 to PERF.4). KICKOFF line 196 already asks for a startup log a person can send. Plan: desktop-shell logs timestamps for process start, single-instance lock, window shown, first page request, app module running, first drawing paint; the About dialog shows the last start's total; owner measures a cold and a warm start on the Windows workstation and the Mac mini with the 0.9 build and records them in the release notes. Budget: window in under 1 s, app usable in under 3 s warm.

– Planned – –
PlatformPERF.15 Link to PERF.15Comment on PERF.15 "Save for offline" downloads 107 to 260 MB per app; 103 MB of CadCraft's 173 MB is compiled help in 52 languages

Measured 2026-11-08 (bin/offline-size.php --libs): CadCraft 0.8.2 offline set 173 MB in 2,077 files: app files 161 MB, libraries 9.6 MB (pdfjs 5.2, three 2.1), shell 2.7 MB. Of the app files, help/ is 103 MB (52 compiled help.<locale>.js of 2 to 3 MB each plus 395 .md sources) and lang/ 33 MB (55 locale files); the modules a person runs are 3.7 MB at the top level. BimCraft 0.8.1 is similar; a Discord member builds reach 260 MB; MarkupCraft 78 MB carries 11.9 MB of Tesseract OCR data. On a phone this is a long download for one language. Plan: the offline set keeps the chosen locale (its lang file and its help.<locale>.js) plus English, never all 52; help .md sources are not saved (the compiled help is what the panel reads); OCR language data per picked language; optional "all languages" switch for a shared classroom machine. AppBuilds::offlineFiles takes the locale (PERF.3 fetches the list on demand). Expected: CadCraft about 35 MB instead of 173 MB, with the same offline behaviour for that person. Test: bin/offline-size.php gains --locale and the service-worker test checks the saved copy opens the help in the picked language.

– Planned – –
Performance roadmap — wave 3 60
PlatformPERF.26 Link to PERF.26Comment on PERF.26 Scale: text writers return one string, so exports stop at 512 MB and hold twice the output

DXF (dxf.js:1324, dxf2000.js:437), LandXML (landxml.js:227), OBJ, DAE, USDA (usda-write.js:33), GeoJSON and gcode writers each build one string and return it; a 1,000,000-entity DXF export is near the V8 string limit and the string plus its Blob is twice the output in memory. Change: writers that yield chunks into a WritableStream (createWritable on a file handle when the host offers one, else a Blob built from parts), with the same report object. Pairs with the streaming readers row.

CadCraftModelCraftCivilCraft App codeOur engines Planned 4 – docs/0.9-sessions/perf-wave3/
PlatformPERF.44 Link to PERF.44Comment on PERF.44 Website: session_activity and analytics_visitors hold 1.6 M rows and 1,015 MB (83 % of the database), 42 % bots, no retention, one table never read

Measured: session_activity 1.02 M rows / 646 MB growing 51,086 rows a day (21,675 of them is_bot = 1), analytics_visitors 597k rows / 369 MB growing 38,644 a day because a cookie-less client gets a fresh visitor id on every request; the table is written on every page view (Analytics.php:75) and read nowhere. Nothing prunes either (no DELETE in bin/ or the classes except the smoke test's own rows). The report queries full-scan the table (EXPLAIN ANALYZE: summary 1.8 s, topPages 2.4 s, each audience group 1.7 s, tryOpens 1.3 s), so the nightly warm job is roughly 60 to 80 s of scans that push the whole table through a 1 GB server-wide buffer pool these two tables alone fill. CadCraft analytics is about 1.1 inserts a second, over half of all INSERTs on this MySQL, each an fsync (two autocommits per page view). At 10x the warm job takes ten minutes and evicts every other database on the server. Fix: (1) do not store bot page views as rows (count per day per bot name in a small table) and give a visitor with no cc_vid cookie no analytics_visitors row until the second request; (2) retention for humans (owner's number; 30 days for any bot rows kept) as monthly partitions by TO_DAYS(created_at) dropped with ALTER … DROP PARTITION, or a nightly batched DELETE on idx_created; (3) a daily rollup table (day, url, country, region, city, device, os, browser, referrer host, campaign, lang, is_bot, views, people) filled for yesterday only, so the dashboard and reports read at most 365 rows per group; (4) stop writing analytics_visitors or read it somewhere; drop the unused idx_session. Keep the privacy page true. Combines the SQL and server tracks (sql-audit.md 4, server-api.md 9).

– Site and services Planned 4 Owner: how long to keep page-view rows (400 days keeps the year-over-year comparison the analytics page offers). docs/0.9-sessions/perf-wave3/
PlatformPERF.50 Link to PERF.50Comment on PERF.50 Website: the Library at 1000x (1 M items, 2.8 M versions, 10 M files): what breaks and the design that does not

Scaling the measured plans linearly from 13,846 items / 27,940 versions / 108k files / 41k metric rows: browse sort plus COUNT plus categoryCounts 64+30+30 ms become 4.6+2.2+2.2 s per page (breaks at about 100x); LIKE search 52 ms becomes 3.7 s while FULLTEXT stays tens of ms (index about 1 GB at 1 M items with descriptions: budget it); the per-card N+1 stays about 450 statements per page but 450 round trips is the floor of every page; publicStats / inUse / makers / forUser full reads 42 ms become 3 s each; possibleDuplicates 159 ms becomes 16 s per /admin/library view; library_metrics at 2 M rows a day needs the daily rollup and a 90-day raw retention; /sitemap (PERF.11) must become a sitemap index of 50k-URL files written nightly, never rendered per request; hot data 5 to 8 GB against a 1 GB server-wide buffer pool (the MySQL settings row). Fine as is: PK and unique lookups (uq_slug, uq_uuid, uq_sha256, uq_item_version), int unsigned ids, the content-addressed file store, the events feed and the notification mailer (idx_feed, idx_mail keep them O(result)). This row is the design pass that ties the Library rows together and sets the data budget before the data arrives (docs/0.9-sessions/perf-wave3/sql-audit.md 12).

– Site and services Planned 5 The Library browse/search/index rows above first. docs/0.9-sessions/perf-wave3/
PlatformPERF.53 Link to PERF.53Comment on PERF.53 Apps: 29.6 MB of byte-identical files under different URLs across the eight releases, and every new release re-downloads 98 % unchanged files

tools/perf/addons-shared.mjs hashed every file of the eight current releases: 665 distinct contents under 2,163 URLs, 29.6 MB extra on the wire for a person who opens more than one app (cabinetcraft add-on in four apps 5.8 MB, dwg/ 3.0 MB, ifc schemas in four apps 2.3 MB, regs in four 2.3 MB, m3d 1.3 MB, crs-db.json 914 KB three times, host.js / addons.js / libitem.js eight times). Across releases: CadCraft 0.8.1 → 0.8.2 kept 2,084 of 2,127 files byte-identical (158 MB) and every one is re-downloaded under the new version URL; the same for every app and every "Save for offline" re-save (PERF.15). Vendor libraries are fine (one URL each, pinCurrent works). Change: a shared /assets/lib/<name>/<content-hash>/ URL for lib/* copies and shared add-ons (release-app.php pins it like vendor paths; the import map aliases ./dwg/x.js to it), or content-hashed file names so an unchanged file keeps its URL across releases. Both shrink the import map, which lists every file because ?v= busting needs an entry per module (499 entries / 48 KB in CadCraft, 772 / 78 KB in BimCraft per page view; AppBuilds.php:391-397).

CadCraftBimCraftModelCraftMarkupCraftTakeoffCraftCivilCraftDetailCraftPartCraft Site and servicesApp shell Planned 8 – docs/0.9-sessions/perf-wave3/
PlatformPERF.58 Link to PERF.58Comment on PERF.58 Apps: an installed add-on package is relinked from blob modules on every start (hvac 334 ms, plant 756 ms) and ships as base64 JSON

addons.js linkPackage turns each verified package into blob: module URLs every start: hvac 0.9.0 (193 files, 1.8 MB; 2.4 MB as base64 JSON) costs 334 ms per start (link 115, import 55, verify 6); plant 756 ms (import 447). The Ed25519 plus sha256 check is cheap (6 to 11 ms for 100 to 193 files); blob modules get no HTTP cache and no V8 code cache, so the cost repeats on every visit and grows with file count. Change: keep the signature check but serve verified packages from the service worker under a stable /addons/pkg/<sha>/<path> scope (or cache the linked texts and import() a persistent URL) so modules get code caching; stop base64 in the package JSON (binary zip, about 34 % smaller). AddonPackages.php, addons.js, sw.js.

CadCraftBimCraftModelCraftMarkupCraftTakeoffCraftCivilCraftDetailCraftPartCraft App shellSite and services Planned 3 – docs/0.9-sessions/perf-wave3/
PlatformPERF.59 Link to PERF.59Comment on PERF.59 Apps: no service worker until someone clicks "Save for offline"; every repeat visit is a cold start minus the 7-day HTTP cache

app-offline.js:160 registers /sw.js only on the click, and sw.js:46 installs nothing on its own; the PWA manifest promises more than the page delivers. Every visit refetches the 830 to 940 KB app page (PERF.2, PERF.3) and relies on the HTTP cache for the 300 to 500 module files (which Vary: Referer defeats, see the server rows). Change: register /sw.js on every /use page; cache-first for immutable URLs (?v= and pinned vendor/app folders: the worker already knows the rule, sw.js:33-40), stale-while-revalidate for the page; "Save for offline" stays the explicit full copy. Pairs with the shared-URL row (a shared lib URL is cached once for all apps) and the installed-package row.

CadCraftBimCraftModelCraftMarkupCraftTakeoffCraftCivilCraftDetailCraftPartCraft App shell Planned 3 – docs/0.9-sessions/perf-wave3/
PlatformPERF.62 Link to PERF.62Comment on PERF.62 Server: the opcache interned-strings buffer is full and the cache restarts during the day

Measured: opcache interned strings 8.0 of 8.0 MB used (70,128 strings), oom_restarts 1 at 20:11 UTC today on a cache started 01:58; 1,195 scripts, 54 of 128 MB. After each restart every PHP file of every vhost recompiles (CadCraft alone 379 scripts / 33 MB): a CPU spike and slow first requests several times a day. Change in /etc/php/8.3/apache2/conf.d/10-opcache.ini: opcache.interned_strings_buffer=32, opcache.memory_consumption=256, then apache2ctl graceful. Healthy otherwise: hit rate 99.94 %, realpath cache 14 % used, autoloader one is_file per class, .env parse 0.04 ms, bootstrap 2 to 3 ms, DB connect 0.8 ms.

– Site and services Decision Time 1 Owner: server-wide PHP setting (shared by about 80 vhosts), no functional risk. docs/0.9-sessions/perf-wave3/
PlatformPERF.66 Link to PERF.66Comment on PERF.66 Server: Apache and TLS settings for a 400-request app start: MaxKeepAliveRequests 100, no OCSP stapling, RSA-2048, no HSTS, MaxConnectionsPerChild 0

Measured: a TLS handshake is 25 to 30 ms on localhost (RSA-2048, session tickets off by certbot, no stapling: "OCSP response: no response sent"); with MaxKeepAliveRequests 100 and six connections a 400-request CadCraft start forces at least four extra handshakes (each about two round trips plus CPU for a visitor). Prefork MaxRequestWorkers 150; 36 to 40 children at 50 MB (81 MB max) = 2 GB; MaxConnectionsPerChild 0 so a mod_php child never recycles. HSTS is on api.getcadcraft.com and the forks, not on getcadcraft.com. Changes: MaxKeepAliveRequests 1000 (or 0) in /etc/apache2/apache2.conf; SSLUseStapling On plus SSLStaplingCache shmcb:${APACHE_RUN_DIR}/stapling(128000) in mods-available/ssl.conf; certbot --key-type ecdsa for getcadcraft.com and api (halves handshake signing); Header always set Strict-Transport-Security "max-age=31536000" on getcadcraft.com; MaxConnectionsPerChild 10000 in mpm_prefork.conf. All moot or smaller after PERF.1 (HTTP/2), which these numbers also strengthen.

– Site and services Decision Time 1 Owner: apache2.conf, mods-available/ssl.conf and mpm_prefork.conf are server-wide (about 80 vhosts); the ECDSA certificate and HSTS header are this site's. docs/0.9-sessions/perf-wave3/
PlatformPERF.67 Link to PERF.67Comment on PERF.67 Server: MySQL settings versus the data on the box: a full 1 GB buffer pool for 5.2 GB of data, table cache overflowing, slow log off

Measured: innodb_buffer_pool_size 1 GB with 1,024 free pages and wait_free 74,540 against 5.2 GB of data plus index across 56 schemas (cadcraft_db 1.23 GB; the two analytics tables alone are 1 GB); table_open_cache 4000 full with 110,482 overflows; Sort_merge_passes 1.1 M with a 256 KB sort buffer; innodb_io_capacity 200 on an SSD; slow_query_log OFF and long_query_time 10 (7 hits in 12 days; nothing below it is known). Also from the SQL track: 64,469 COMMITs at 2.76 ms (innodb_flush_log_at_trx_commit = 1, every autocommit an fsync) and SET time_zone run 1.28 M times = one per connection (no persistent connections). Changes: innodb_buffer_pool_size 4G (SET GLOBAL plus mysqld.cnf), table_open_cache 8000, sort_buffer_size 1M, innodb_io_capacity 1000 / max 4000, slow_query_log ON with long_query_time 0.5 and log_queries_not_using_indexes capped, init_connect = "SET time_zone='+00:00'" if persistent connections are wanted; flush_log_at_trx_commit 2 is a server-wide trade (a second of rows lost on a crash) the other sites would share: the analytics row removes most of the fsyncs anyway. Healthy: pool hit rate 99.9 %, 2 disk temp tables of 3.6 M, redo 430 MB a day, max_used_connections 44 of 151.

– Site and services Decision Time 1 Owner: shared MySQL (56 schemas); all settings are dynamic, no restart; the pool adds about 3 GB to mysqld's 2.8 GB RSS (15 GB free). docs/0.9-sessions/perf-wave3/
PlatformPERF.68 Link to PERF.68Comment on PERF.68 Server: disk at 75 % with 90 GB reclaimable (a 56 GB Rust build tree, tmp/ build trees, both 0.8 downloads, a lang-overlay cache); logs rotated monthly

Measured: / is 290 of 388 GB. desktop-shell src-tauri target 56 GB (the forks' target moved to W: on 2026-11-08; this one did not; memory rule: never rebuild forks on the VPS), stores/build 18 GB, tmp build trees about 13 GB (editions, public-repos, studio-suite-0.9-a Discord member, devshell, live-apps-www*), downloads 0.8 and 0.8.1 both on disk (2.1 GB each; 0.7 and older moved to the CDN), three byte-identical frozen copies per app (hardlinking would save about 1.3 GB), tmp/cache/lang-overlay 981 MB with no TTL; access.log 345 MB a day uncompressed for a month (2.76 GB now). Noise: mec-broker logs two lines a minute when idle, a Discord member-sandbox-sync logs the same schema drift (team_join_codes.used_at) every 10 minutes, mail-poll logs a PHP array_merge(stdClass) error every 5 minutes (a ContactMail bug). Changes: move or delete the Rust target, clean tmp/ after each release, TTL on lang-overlay, hardlink identical files across frozen versions in bin/release-app.php, publish 0.8 downloads to the CDN like 0.7, weekly (or daily access) logrotate, quiet the idle broker line, fix the sandbox drift and the mail-poll error. About 90 GB back (75 % to about 52 %).

– Site and servicesTests and tooling Planned 1 Owner: /etc/logrotate.d is server-wide; tmp/downloads-replaced (4.7 GB) is the "never deleted" policy and stays. docs/0.9-sessions/perf-wave3/
Suggest an item

Anyone can send one; no account and no name needed. The team reads each idea, and the ones we take on appear on this page. Please leave out web addresses.