Uh oh!
There was an error while loading. Please reload this page.
feat(installer): node-local storage prototype (RFC-0003 Option C, flag-gated) - #368
Conversation
…g-gated) TB_STORAGE_MODE=node-local (default: hostpath, unchanged) switches the local k3d install to store datasets on k3s's built-in local-path provisioner INSIDE the node instead of bind-mounting ~/.tracebloc. Data then dies with `cluster delete`, is not a browsable host folder, and needs no chmod 777 — the RFC-0003 goal for the local install. - common.sh: TB_STORAGE_MODE flag + validation; C1 forces AGENTS=0 (single-node) since local-path is RWO/WaitForFirstConsumer and the shared data PVC is mounted by jobs-manager-spawned Jobs. - cluster.sh: node-local drops the -v ~/.tracebloc:/tracebloc@all bind-mount, KEEPS k3s local-storage (removes --disable=local-storage), and skips the world-writable host-dir pre-create. - install-client-helm.sh: node-local emits storageClass.create=false + name=local-path, hostPath.enabled=false; skips _ensure_release_dirs. - cluster.bats: cover both modes. Default (hostpath) path is byte-for-byte unchanged; existing installs on --reuse-values do not migrate. Refs #367, backend#1151, tracebloc/cli#366. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
saadqbal
commented
Jul 22, 2026
Verified on a real Mac (k3d v5.8.3, k3s v1.31.5, docker desktop)Ran this against a throwaway single-node cluster built with the exact args node-local emits (no Chart rendering (
Live storage mechanics:
So the RFC-0003 goal is proven at the substrate level: data sandboxed in the node, survives restart, destroyed on delete, never a browsable host folder. Still needs a credentialed run (can't do without client creds): a full |
…local
install_client_helm() calls _ensure_tracebloc_dirs independently of
create_cluster; the first gate missed it, so node-local installs still
created empty world-writable ~/.tracebloc/{data,logs,mysql} dirs. Now
node-local only ensures the base dir exists (for values.yaml + the install
log) and skips the 777 data subdirs. Verified on a real tb-nodelocal
install. Refs #367.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>saadqbal
commented
Jul 22, 2026
Full validation on a real credentialed install ( |
Resolves the manifest.sha256 conflict by regenerating it over the merged file set (scripts/gen-manifest.sh). The hand-written libs (cluster.sh, common.sh) and cluster.bats auto-merged cleanly. Verified post-merge: - bats scripts/tests/cluster.bats — 30/30 (node-local tests 13/14 pass, plus develop's new Tier-0 autostart test) - gen-manifest.sh --check — up to date - shellcheck cluster.sh/common.sh/install-client-helm.sh — no new findings - common.bats: the one failure (validate_config) reproduces on clean origin/develop → pre-existing sandbox limitation, not from this merge Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The node-local storage flag (RFC-0003 Option C) was undocumented in the env-var overrides list. Add it alongside HOST_DATA_DIR with a one-line note on what node-local does (k3s local-path, no host dirs, AGENTS=0). Regenerated manifest.sha256 (install-k8s.sh is manifest-covered). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Two silent-misroute footguns Bugbot flagged on the node-local path: 1. node-local + HOST_DATASET_DIR: node-local forces hostPath.enabled=false, so a HOST_DATASET_DIR network export was accepted but ignored — datasets would land on ephemeral local-path storage (gone on 'cluster delete'). Combining the two is a documented follow-up (backend#743 + RFC-0003); validate_config now rejects the combo instead of misrouting. 2. Reusing a cluster built for the other storage topology: storage mode is baked in at create time (hostpath bind-mounts /tracebloc + disables k3s local-storage; node-local does neither). A node-local install onto a hostpath cluster requests a local-path StorageClass that was disabled (PVCs Pending); a hostpath install onto a node-local cluster points hostPath PVs at an unmounted /tracebloc (ephemeral). New _check_existing_cluster_storage_mode fails fast both ways, mirroring _check_existing_cluster_dataset_mount. The /tracebloc bind mount is the discriminator: present <=> hostpath cluster. Tests: cluster.bats 35/35 (5 new storage-mode cases), common.bats +1 (node-local + HOST_DATASET_DIR rejected). shellcheck: no new findings. Regenerated manifest.sha256 (cluster.sh + common.sh are manifest-covered). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit ab57754. Configure here.
Uh oh!
There was an error while loading. Please reload this page.
…0 (Bugbot #368) The C1 single-node guarantee only forced AGENTS=0. But k3s server nodes are schedulable (unlike a full k8s control plane), so TB_STORAGE_MODE=node-local with SERVERS>1 still produced multiple nodes — the RWO local-path data PVC binds on one while jobs-manager Jobs land on another and stay Pending, the exact failure C1 exists to prevent. Now clamps both AGENTS=0 and SERVERS=1. Tests: common.bats +2 (node-local clamps both; hostpath leaves them untouched). shellcheck: no new findings. Regenerated manifest.sha256. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
shujaatTracebloc
left a comment
There was a problem hiding this comment.
Review — node-local storage prototype (RFC-0003 Option C)
Strong, low-risk PR — flag-gated, default hostpath unchanged, and unusually well-documented. I verified the load-bearing bits rather than eyeballing them:
Verified ✅
- Signed manifest is correct — the #1 thing to get right on an installer PR. Recomputed sha256 for all 18 manifest entries against the committed files: every hash matches, including all four changed scripts. A stale hash here would silently break bootstrap signature verification for every user; this is clean. (The scripts not in the manifest —
install.sh/install.ps1/gen-manifest.sh/… — are the bootstrap + dev-tooling, correctly excluded; unchanged by this PR.) - Default hostpath is byte-for-byte preserved — the hostpath branch emits the same k3d args in the same order;
--waitwas just moved to a shared+=after the if/else, so ordering is identical. - Tests pass —
bash -nclean on all four scripts; every new bats case is green:_create_new_clusternode-local (no bind-mount, keeps local-storage) + hostpath control, all five_check_existing_cluster_storage_modecases (both match directions, both mismatch fail-fasts, inspect-fail no-op),validate_confignode-local+HOST_DATASET_DIR rejection, and the C1 clamp both ways. (One local common.bats failure —validate_config: valid config passes— is a pre-existing macOS-tmpdir artifact: it passes in CI and validate_config exits 0 in a manual repro with the same fixture; unrelated to this PR.) - CI green (E2E last-mile correctly skipped — needs a real machine).
Nice touches: the fail-fast topology guard with actionable recreate remedies (both directions), the HOST_DATASET_DIR+node-local rejection (prevents a silent misroute to ephemeral storage), and the C1 rationale (k3s servers are schedulable, so SERVERS=1 is needed too, not just AGENTS=0) are all correct and well-explained.
Non-blocking notes
- The helm-values rendering branch isn't unit-tested. The bats cover cluster args, the storage-mode guard, and the C1 clamp — but not the rendered
values.yamlblock (storageClass: {create:false, name:local-path}+hostPath.enabled:false). It's built inline in a heredoc, so a future edit could silently break the node-local YAML and only surface at install time. You manually rendered both modes (good) — worth a follow-up to extract the values render into a testable function (or add a render-assertion test) so it's guarded. - The C1 clamp is silent.
AGENTS=4 SERVERS=3+ node-local gets clamped to0/1with no message. node-local genuinely requires single-node, so the clamp is right — but a one-linewarn("node-local forces single-node; ignoring AGENTS/SERVERS") would beat a silent override for anyone testing the flag. - Scope reminder (not a defect): node-local is verified only for helm-template (item 1); items 2–6 (real install → ingest → train → stop/start survival → delete=gone → hostpath upgrade) need a real machine and are correctly unchecked. Merging is safe because the default path is untouched, but node-local shouldn't be treated as production-ready until that matrix runs.
LGTM to merge as a flag-gated prototype. 🚀
🤖 Reviewed with Claude Code
shujaatTracebloc
left a comment
There was a problem hiding this comment.
Approving — flag-gated, default hostpath byte-for-byte unchanged, signed manifest verified (all 18 hashes match), new bats all green. Details in my review comment above.
Non-blocking follow-ups: (1) the node-local helm-values heredoc isn't unit-tested — worth extracting to a testable function; (2) the C1 single-node clamp is silent — a one-line warn would be friendlier. Neither blocks; node-local's end-to-end matrix (install/train/delete/upgrade) still needs a real machine, but the default path is untouched so this is safe to land. 🚀
🤖 Reviewed with Claude Code
Uh oh!
There was an error while loading. Please reload this page.
LukasWodka
commented
Jul 25, 2026
Functional review \u2014 passed\n\nBasis: the behavioural suites that ran on this PR at merge against real environments, not mocks:\n\n- on multiple Ubuntu releases + \u2014 a real install exercised end-to-end on real distros, behind a TLS-inspecting proxy\n\nI could not reach the dev API (no dev credentials), so rather than rubber-stamp I used the strongest evidence available: these suites exercise the actual behaviour this change alters, on real infrastructure. Advancing \u2192 .\n\nIf the functional reviewer wants a manual pass on dev in addition, please move it back and say so. |

Prototype — flag-gated behind
TB_STORAGE_MODE, defaulthostpath(today's behavior, byte-for-byte unchanged). Opens the RFC-0003 Option C (node-local) path for testing on a real machine.Implements the storage half of client#367. Design: backend#1151. RFC: tracebloc/cli#366.
Goal (Asad + Lukas)
Deleting the cluster must destroy dataset data, and data must not sit as a browsable folder in
~/.tracebloc— it should live inside the cluster's sandbox.What
TB_STORAGE_MODE=node-localdoeslocal-pathprovisioner inside the k3d node → data dies withcluster delete, is not a host folder, needs nochmod 777.-v ${HOST_DATA_DIR}:/tracebloc@allbind-mount and keeps k3slocal-storage(the installer disables it in hostpath mode because the chart ships its ownmanualStorageClass).storageClass.create=false+name=local-path,hostPath.enabled=false(reuses the chart's existing dynamic-PVC path — same one EKS/AKS use). The mysql privileged chown init-container auto-drops whenhostPath.enabled=false.AGENTS=0(single-node).local-pathis RWO + WaitForFirstConsumer and provisions on one node, but the shareddataPVC is mounted by jobs-manager-spawned Jobs that could otherwise land on a second node with no volume.Not in scope / notes
hostpathpath unchanged; existing installs upgrade via--reuse-valuesand do not silently migrate (they move to node-local only on a clean delete + reinstall — matches "delete means gone", no data-copy tool needed).chmod 777removal is not a separate PR: on the hostpath model it's load-bearing (host-user writes into/data/shared+ kubelet doesn't apply fsGroup to hostPath volumes), so it can't be dropped standalone safely. node-local removes the host dirs entirely, so 777 is gone by construction here.HOST_DATASET_DIR(network-mount datasets, backend#743) is left on the hostpath path for now; combining it with node-local is a follow-up.CI / static checks
bats scripts/tests/cluster.bats— 30/30 (incl. 2 node-local tests: node-local omits mount + keeps local-storage; hostpath keeps both).bats scripts/tests/common.bats— pass.shellcheck— no new findings.HOST_DATASET_DIRcase → valid YAML.Verification status (checklist from client#367)
helm templatewith node-local values → verified: no hostPath PV, all PVCs bindlocal-path, no chart-created StorageClass, no mysql chown init-container. (hostpath control renders 3 PVs /*-storage-class/ 1 StorageClass / chown init-container — the expected contrast.)Remaining items need a real machine (k3d + Docker; can't run in CI):
TB_STORAGE_MODE=node-localinstall → ingest a dataset → confirm nothing under~/.tracebloc, pod mounts fine.k3d cluster stop && start→ data survives.tracebloc delete(and a rawk3d cluster delete) → data gone.Note
Medium Risk
Touches cluster creation and persistent volume wiring—misconfiguration could leave PVCs Pending or data on ephemeral storage—but default hostpath is unchanged and new validation plus existing-cluster checks reduce silent failure modes.
Overview
Adds
TB_STORAGE_MODE(hostpathdefault, unchanged behavior;node-localopt-in) so local installs can keep datasets on k3slocal-pathinside the k3d node instead of bind-mounted~/.tracebloc.With
node-local, the installer skips host data dir prep andchmod 777, creates k3d without the/traceblocbind-mount and without disabling k3s local-storage, forces single-node (AGENTS=0,SERVERS=1), and renders Helm values withhostPath.enabled: falseandstorageClass.name: local-path. Reusing an existing cluster now fails fast if host topology (presence of/traceblocmount) does not match the requested mode.HOST_DATASET_DIRis rejected when combined with node-local.Reviewed by Cursor Bugbot for commit 2f10fea. Bugbot is set up for automated code reviews on this repo. Configure here.