RFC-0016 — Deployment, Vendoring & Topology¶
| RFC | 0016 |
| Title | Deployment, Vendoring & Topology |
| Slug | deployment-vendoring-topology |
| Status | Accepted |
| Track | Standards |
| Authors | @AbdelStark |
| Created | 2026-06-02 |
| Target milestone | Decisions: v0.1; rollout across v0.1–v0.3 |
| Area | core |
| Requires | RFC-0001, RFC-0009, RFC-0011, RFC-0013 |
| Informs | RFC-0003, RFC-0005, RFC-0010 |
Summary¶
This RFC ratifies four foundation decisions that the rest of the corpus assumes but did not record: the
implementation is Python-first (including distributed-training orchestration), with a documented Rust
contingency; the reused ecosystem code (stable-worldmodel, stable-pretraining) is vendored into a
third_party/ tree so it can be modified in place to ship a proof of concept quickly; the project runs
on a single deployment substrate model — one configuration source rendered to an in-process
simulation, Docker Compose, and Kubernetes — over the single RFC-0013
Transport seam; and the infrastructure is defined as code. It is the decision record behind issue
#96. External-ecosystem facts cited here are
research findings and are marked confirm against upstream where not yet pinned in-tree.
Motivation¶
The corpus specifies what Lensemble builds and how the science works, but leaves the engineering
substrate implicit: which language the orchestration is written in, how the heavily-reused
stable-worldmodel data/eval layer is integrated, how a contributor runs N nodes on a laptop, and how
the same system reaches a production cluster. Re-litigating these per subsystem is slow and produces
drift. The proof-of-concept goal demands the ability to modify the reused code in place now (a fork
is not used yet) while staying clean enough to contribute upstream later. These are cross-cutting
choices that span RFC-0001 (topology, layout, dependencies),
RFC-0013 (the runtime and its transport), and
RFC-0009 (the config model), so they are recorded once here
rather than amended into three Accepted RFCs in place (see Alternatives Considered).
Goals¶
- Lock Python as the implementation language for the whole stack, including outer-loop orchestration, with named, measurable conditions under which a partial Rust port is reconsidered.
- Define a
third_party/vendoring policy that keeps vendored code modifiable, auditable against upstream, license-clean, and isolated from the public dependency DAG. - Add
stable-pretrainingas a declared dependency and reconcile the dependency pins the reused code requires against conventions §11. - Define a one-config-source deployment model:
LensembleConfigrenders to the in-process simulation, Docker Compose, and Kubernetes, all driving the identical RFC-0013 round state machine. - Keep every choice consistent with the named invariants (
INV-AGG-DETERMINISM,INV-RESIDENCY, the no-cycle layering DAG) and with the staged plan (conventions §12).
Non-Goals¶
- Implementing the runtime, transport, or secure aggregation themselves — those stay with their issues and RFCs (RFC-0013, RFC-0011).
- Writing the
pyproject.toml(issue #71) or the Hydra config schema (issue #34); this RFC specifies the decisions and amendments they apply. - Building a Rust kernel — recorded only as a triggered contingency.
- Mandating a specific cloud or Kubernetes distribution; this RFC fixes the model and the tooling classes, not a vendor.
- The Stage-D verifiable layer; only the Phase-1 proof-ready disciplines constrain the substrate.
Proposed Design¶
1. Language and frameworks: Python-first¶
The implementation is Python (>=3.11, conventions §11),
end to end, including the orchestration of distributed training.
- Inner loop (intra-participant), for scale. PyTorch FSDP2 — the
fully_shard/ DTensor /MixedPrecisionPolicy(bf16 compute, fp32 reduce) surface intorch>=2.4(the exact symbol paths are pinned by issue #71) — launched withtorchrun. This is the only place the large-model-parallelism playbook applies (RFC-0001 §5). - Outer loop (inter-participant), for sovereignty. A Python DiLoCo implementation (inner AdamW, outer
Nesterov SGD over pseudo-gradients) driven by the
RFC-0013
Coordinator/Participant/RoundStatemachine behind theTransportProtocol. Rationale: DiLoCo communicates only everyHinner steps (RFC-0003 §2), so the control plane is I/O-bound and off Python's hot path; OpenDiLoCo demonstrated this regime at billion-parameter scale with single-digit communication overhead (research finding; confirm against upstream).
Risks to monitor (named budget bands; relate to
08 — Performance Budget and the metrics of
RFC-0015). The GIL can serialize work only if the coordinator
does heavy Python-side tensor work on the aggregation critical path; this is low-risk because
RFC-0013 §4 keeps the
reduction single-threaded in fixed canonical order anyway (INV-AGG-DETERMINISM). The other watch item
is per-round Δ_c serialization/masking cost as the encoder scales from ViT-L toward 1.2B.
fed/round_overhead_ms(non-compute round wall time), serialize+deserialize time per round,fed/comm_bytes(already a CI perf smoke), coordinator Python-thread utilization, and scheduling jitter vs the contributing-set size.OPEN QUESTION:the breach thresholds are set by measurement at Stage A/B, not asserted here (owner @AbdelStark).- Mitigation ladder, applied in order before any language change: int8/FP16 pseudo-gradient quantization (RFC-0003 §6) → free-threaded CPython (PEP 703/779; non-default, ~5–10% single-thread cost — research finding) → vectorize / native extension → Rust.
Rust/Burn contingency (deferred, triggered). A partial, kernel-level Rust port is reconsidered only
when (1) a monitored Python path on the aggregation/serialization critical path is the proven
bottleneck after the ladder above is exhausted, or (2) the Phase-2 proof surface
(RFC-0006 §2) wants a
small, auditable, memory-safe reduction kernel. Burn (Rust, swappable backends, loads safetensors/
PyTorch weights — confirm against upstream) is the candidate so RFC-0010
artifacts survive the seam; tinygrad is recorded as a philosophy/edge idea, not a near-term option. A
wholesale inner-loop rewrite is out of scope: it forfeits FSDP2, the V-JEPA 2 warm-start
(INV-WARMSTART-T0), and the stable-worldmodel data/eval layer for no proven benefit.
Tapestry: concepts, not primitives. Project Tapestry (AI Alliance) informs the sovereignty/governance
framing and the rigid world-model training-regime vocabulary only; Lensemble keeps its own objective and
model authority (RFC-0002/RFC-0008),
since Tapestry targets a frontier LLM with no latent-gauge concern. RISK: the corpus previously assumed
Tapestry is C++; research indicates its product code is Python (torch + numpy) with Proposed-status ADRs
(confirm against upstream). Either way the Python-first decision stands on its own merits.
2. Vendoring: third_party/ as modifiable, license-clean subtrees¶
Add a third_party/ tree at the repo root holding each reused project at a recorded upstream commit, so
Lensemble can modify the code in place to ship the PoC fast. This is the ratified target (a fork is not
used now); a pinned PyPI/VCS dependency ("treat them as libraries") is the fallback, used only where
a constraint blocks committing source. This is a layout addition relative to
conventions §1 and
RFC-0001 §2, ratified by this RFC;
third_party/ is a separately-licensed subtree and is outside the lensemble import DAG of
RFC-0001 §3.
third_party/
stable_worldmodel/ # vendored at a recorded SHA; data layer + envs + latent-MPC eval
UPSTREAM.md # source URL, vendored SHA + date, license SPDX + in-tree LICENSE path, mod log, sync procedure
patches/*.patch # local modifications, applied over a pristine snapshot (tree stays byte-identical to upstream)
stable_pretraining/ # vendored at a recorded SHA; pretraining scaffold
UPSTREAM.md patches/*.patch
- Both projects are vendored now.
stable-pretrainingships a real MITLICENSE(confirm at vendor time).stable-worldmodel's repository was missing aLICENSEfile despite an MIT classifier in its metadata; the maintainers have confirmed the license is MIT and that the missing file is a packaging mistake they will correct, and confirmed it is usable today — so it is vendored now, with theUPSTREAM.mdmanifest recording the MIT SPDX, the maintainer confirmation (date + source), and a note that the in-treeLICENSEfile is to be synced from upstream once published. No upstream LICENSE issue is opened by Lensemble (the maintainers own the fix). - Modifications live as a patch series (
third_party/<project>/patches/*.patch) over a pristine snapshot, never edited in place, so the vendored tree stays byte-identical to upstream at the recorded SHA, re-syncing is a clean re-clone + re-apply, and the diff against upstream is always inspectable (consistent with conventions §9 and RFC-0006 §3). - Import boundary / shim. A thin internal adapter (the exact module path is for issue #2 to fix —
e.g. an adapter under
lensemble.datafor the episode/format readers of conventions §8 and underlensemble.evalfor theworld.evaluate+ planner factory of RFC-0005 §3) imports fromstable_worldmodel/stable_pretrainingand re-exposes only the surface Lensemble depends on. Nothird_partysymbol is re-exported fromlensemble.__init__, confining upstream API churn to one module and preserving the no-cycle DAG. - Upstream-contribution path. Keep an upstream remote; re-vendor by bumping the recorded SHA and
re-applying patches; contribute fixes back so the local patch set shrinks over time. The procedure is
documented in
UPSTREAM.mdso a contributor re-syncs without messaging the author (conventions §13).
3. Dependencies reconciled¶
conventions §11 and the 09 — Release & Versioning dependency table are amended (each to confirm against upstream):
| Dependency | Constraint | Reason |
|---|---|---|
| stable-pretraining | pinned (vendored SHA) | pretraining scaffold reused alongside stable-worldmodel; previously undeclared |
RISK: the following are unverified research leads, reconciled when the vendored SHAs are confirmed —
do not pin until verified at vendor time: stable-worldmodel may require lancedb>=0.30.0 /
pylance>=4.0.0 at runtime against the looser lance>=0.10 currently pinned; and its lerobot://
adapter may require Python >=3.12 against the current >=3.11 floor. Decision: keep the >=3.11 floor
and treat the lerobot adapter as out of Stage-A scope (the lance/hdf5 paths cover Stage A,
RFC-0004 §1); revisit the floor only if
lerobot enters scope. Candidate vendored SHAs are research leads recorded in UPSTREAM.md, not pinned
here.
4. Deployment: one config source, three substrates, one transport¶
LensembleConfig (RFC-0009 §2)
is the single source of truth; every substrate is a renderer over it (participant count → replicas,
FederationConfig.transport → service wiring). All three substrates drive the identical RFC-0013 round
state machine over the one Transport Protocol
(RFC-0013 §5); this RFC extends
that seam with Compose and Kubernetes materializations, it does not invent a new one.
- Layer 0 — in-process simulation (default; Stage B /
v0.2). An event-drivenInProcessTransportthat is deterministic, CPU-runnable, and fast. It is the canonical test/CI harness and the substrate the paper's ablation ladder runs on; it guardsINV-AGG-DETERMINISMon CPU. The multi-process/ container path is a superset, not a replacement — the sameCoordinator/Participant/RoundStatecode runs over a real transport. - Layer 1 — Docker Compose (local multi-node). One
compose.yamlusingprofilesto select subsets (acoordinatorservice + aparticipantservice scaled viadeploy.replicas), agpuprofile (GPU passthrough viadeploy.resources.reservations.devices,capabilities: ['gpu']) and acpuprofile for CI/laptops, andhealthcheck/depends_onso participants join only after the coordinator is ready (transport='network'over loopback). Generated fromLensembleConfig.federation, not hand-maintained. - Layer 2 — Kubernetes (production; Stage C+). A coordinator Deployment/Job + a participant set
(StatefulSet or a JobSet replicated group). The two-level topology of
RFC-0001 §5 maps onto K8s primitives as
inner = gang-scheduled (a participant's FSDP/TP pod group: Volcano/Kueue all-or-nothing admission
is correct there; Kubeflow Trainer v2 (
TrainJob/TrainingRuntimeon JobSet) and KubeRay (RayJob/RayCluster) are candidate inner-loop runtimes, evaluated not mandated) and outer = elastic, NOT gang-scheduled across participants, because a round proceeds with a quorum (RFC-0013 §3). The TEE-attested aggregator backend (RFC-0011 §5) implies a confidential-compute node pool — a documented Stage-C requirement. - IaC tooling. Helm packages the coordinator+participant deployment (versioned chart, rollback);
Kustomize overlays per environment (local kind/minikube, staging, sovereign-prod). Cloud provisioning
uses Pulumi-in-Python (Python-first; typed Kubernetes resources) with Terraform documented as the
alternative. Every IaC artifact is parameterized from the same
LensembleConfig, so config → simulation/Compose/Kubernetes is one pipeline.
5. Invariants the substrate must preserve¶
INV-AGG-DETERMINISM— no transport may introduce nondeterministic reduction; the coordinator sums in canonical participant order regardless of arrival order (RFC-0013 §4). The determinism self-check (RFC-0009) is the backstop.INV-RESIDENCY— moving from one process to many containers/pods makes raw-data egress a real risk; RFC-0009 §3 forcesdata.residency_enforced=Truewhentransport=='network', andResidencyViolationis fail-closed. K8s overlays must not co-locate participant data volumes across trust domains.- The no-cycle import DAG (RFC-0001 §3) —
vendored
third_partycode stays outside it, reached only through the shim.
Alternatives Considered¶
- Depend-not-vendor (use as libraries). Cleaner dependency hygiene, but it forfeits in-place modifiability, which is the stated PoC requirement (the reused code needs local changes now). Kept as the fallback for any project a constraint blocks from source-vendoring.
- Fork the upstream repos. Rejected for now: a fork is heavier to track against the canonical repo and the decision is explicitly to integrate directly while staying clean; the patch-series-over-pinned- SHA approach gives the same auditability without a separate fork to maintain.
- Rust-first (Burn/tinygrad) implementation. Rejected: it forfeits the PyTorch/V-JEPA 2/ stable-worldmodel ecosystem the project is built on, for a perf concern that is unproven at PoC scale. Retained as a triggered, partial-port contingency.
- Ray as the outer-loop orchestrator. Powerful (cluster scheduling, fault recovery) but a heavy
dependency, and its lineage-based recovery and actor scheduling are not verified to preserve
INV-AGG-DETERMINISM. Deferred: adopt only if a hand-rolled coordinator outgrows its scope, and only after verifying the determinism contract. KubeRay remains a candidate inner-loop runtime. - Gang-schedule everything on Kubernetes. Rejected for the outer loop: all-or-nothing admission contradicts the elastic quorum-completion contract (RFC-0013 §3). Gang scheduling is correct only inside a participant's inner pod group.
- Terraform instead of Pulumi. Broader provider catalog and registry maturity, but HCL is a separate language; Pulumi-in-Python keeps IaC in the project's language and can read existing Terraform state for incremental adoption. Terraform documented as the alternative.
- Amend RFC-0001 + RFC-0013 in place instead of a new RFC. Rejected: the decisions span two Accepted RFCs and introduce dependency/vendoring concerns that belong to neither cleanly; a standalone decision record is easier to review and supersede than scattered in-place edits.
Drawbacks¶
- Vendoring adds maintenance: the patch series must be re-applied on each upstream bump, and a stale vendored tree can mask upstream fixes. Mitigated by the drift CI guard and a shrinking patch set as fixes land upstream.
- All-Python orchestration carries the monitored perf risk of §1; the mitigation ladder is the hedge.
- Supporting three substrates is surface area. Mitigated by staging strictly (the paper needs only the in-process simulation) and by the single-config-source model so the substrates share one code path.
Migration / Rollout¶
Staged to conventions §12. v0.1: ratify these
decisions; add third_party/ + the vendoring manifests + the import shim; amend the dependency table;
land the Compose cpu profile for local dev. v0.2: the in-process simulation is the only substrate the
scientific core needs; Compose is a developer convenience. v0.3 (Stage C): the networked transport
(issue #45), the Kubernetes manifests (Helm + Kustomize), and the confidential-compute node pool for the
TEE backend. The Rust contingency is out of all v1.0 milestones unless a trigger condition fires.
Testing Strategy¶
- Vendoring-drift guard (CI, relate to #65/#66): a test asserts each vendored tree equals its
recorded upstream SHA with
patches/*.patchapplied — no silent local drift. CPU-only, tiny fixtures (conventions §9). - License-presence guard: CI fails if
third_party/<project>/source is present without an in-treeLICENSEat the recorded SHA (enforces the license discipline rather than relying on review). - No-leak / layering test (owned with #2): asserts no
stable_worldmodel/stable_pretrainingsymbol is re-exported fromlensemble.__init__and the RFC-0001 §3 no-cycle DAG holds. - Substrate-parity test: the same federated round produces the identical committed global-model hash
under
InProcessTransportand under the Compose loopback transport (transport portability does not perturbINV-AGG-DETERMINISM). - Compose smoke:
docker compose --profile cpu upbrings upNparticipants + 1 coordinator on loopback and completes one round;helm template/kustomize buildrender without error. - Config-render test:
LensembleConfig.federationwithparticipant_count=Nrenders a Compose file withNparticipant replicas and a K8s manifest set with the matching topology.
Open Questions¶
OPEN QUESTION:the numeric monitoring thresholds (fed/round_overhead_msetc.) that trigger each mitigation step — set by measurement at Stage A/B (owner @AbdelStark; resolves in 08 — Performance Budget).OPEN QUESTION:whether Ray (KubeRay/Ray Train) is adopted as the inner-loop runtime, contingent on verifying it preservesINV-AGG-DETERMINISM(owner @AbdelStark; Stage C).OPEN QUESTION:the inner-loop runtime choice on Kubernetes — baretorchrunvs Kubeflow Trainer v2 vs KubeRay (owner @AbdelStark; Stage C).OPEN QUESTION:reconciling thelance/pylancepins and thelerobotPython->=3.12requirement against the>=3.11floor, once the vendored SHA is confirmed (owner @AbdelStark; Stage A).- The control-plane transport (gRPC vs HTTP) is deferred to RFC-0013 Open Questions.
References¶
- Corpus: RFC-0001 (topology, layout, dependency layering),
RFC-0013 (
Transport, elasticity, determinism), RFC-0009 (the config model + residency validation), RFC-0011 (aggregator backends), RFC-0003 §6 (quantization), conventions §1/§9/§11/§12. - Issue #96 — the foundation issue this RFC records.
- Prior art (research findings; confirm against upstream): V-JEPA 2;
stable-worldmodel/stable-pretraining(galilai-group); LeJEPA/LeWM (SIGReg); DiLoCo / OpenDiLoCo / INTELLECT (Prime Intellect); Project Tapestry (AI Alliance); PyTorch FSDP2; Kubeflow Trainer v2; Volcano; KubeRay; Helm / Kustomize; Pulumi; Burn; tinygrad; free-threaded CPython (PEP 703/779).