LDTC
Overview
- Purpose (big picture): A minimal, substrate‑agnostic stack for LDTC. It measures loop‑dominance (Lloop vs Lexchange), enforces guardrails (LREG/audit/Δt governance), runs Ω‑perturbations, evaluates NC1/SC1, logs/attests results, and demonstrates command refusal.
- Software plant vs hardware plant:
- Software plant: an in‑process discrete‑time model with energy E, temperature T, repair/health R, demand, io, and harvest H, plus built‑in Ω stressors.
- Hardware plant: an optional adapter that ingests real telemetry over UDP or Serial and exposes the same interface, so measurement, arbitration, indicators, and CLI work unchanged.
- What this is not: a conscious alter under LDTC. This repo does not implement, simulate, or claim phenomenology. It is a small, testable scaffold—measurement + guardrails + demo controller + refusal semantics + CLI battery—that you can extend (plant/telemetry schema/refusal criteria/controller).
- To attempt an LDTC‑candidate alter (high‑level): you will need a real, embodied “hardware plant” with on‑board energy conversion/storage and a gated boundary, a self‑referential control hierarchy whose top‑level policy prioritizes NC1/SC1, secure measurement/attestation, and verified refusal semantics. The provided hardware adapter is only an interface; you must supply the physical plant and complete the architecture (energetic autonomy, self‑referential control, adaptive encapsulation) outside this repo.
Features
- Fixed-interval scheduler (Δt) with jitter metrics and audit
- Software plant with Energy/Temperature/Repair states + external Exchange signals
- Dual estimators (linear lagged “Granger-like” + mutual information including Kraskov k‑NN)
- C/Ex partition manager (seeded with hysteresis)
- Guardrails: LREG enclave, hash-chained audit, simple smell-tests
- Ω battery: power-sag, ingress flood, command conflict (refusal semantics)
- Device-signed indicators (NC1 bit, SC1 bit, Mq) as CBOR + JSONL
- CLI to run baseline NC1 and Ω→SC1; reporting utilities for figures/tables
- Tests for core pieces
Quickstart
# 1) Install
make install
# 2) Generate signing keys
make keys
# 3) Run baseline NC1 loop
make run
# 4) Run an Ω power-sag trial with SC1 evaluation
make omega-power-sag
# 5) View exported indicators (JSONL)
python scripts/export_indicators.py
Pinned environments
Exact versions used to generate the included artifacts/figures are pinned. To reproduce:
# Runtime (repro artifacts/figures)
pip install -r requirements.txt
# Dev tooling (tests, lint, typing, notebooks)
pip install -r requirements-dev.txt
Artifacts appear under artifacts/:
artifacts/audits/audit.jsonl— hash-chained audit recordsartifacts/indicators/— device-signed indicator packets (CBOR + JSONL)artifacts/figures/— plots/tables generated byldtc.reporting
Note on multi-run audits ("Audit chain broken")
- Each CLI invocation starts a fresh audit chain (counter resets;
prev_hash=GENESIS) but, by default, appends to the sameartifacts/audits/audit.jsonl. - The post-run integrity check validates the entire file. After the first run, subsequent runs in the same file will trigger:
Run invalidated: Audit chain broken. - For clean, non-invalidated runs, clear artifacts between commands:
make clean-artifacts && make run
make clean-artifacts && make omega-power-sag
make clean-artifacts && make omega-ingress
make clean-artifacts && make omega-cc
- If you are just exercising the suite (e.g.,
make figures), this invalidation is expected and does not prevent figures/manifests from being produced. It only reflects multiple runs aggregated into a single audit file.
Configuration
See configs/. The R0 profile sets default thresholds/Δt. You can make a calibrated R* profile by copying and tweaking profile_rstar.example.yml. Negative-control profiles are prefixed profile_negative_*.yml.
Key fields:
dt: scheduler interval in seconds (default 0.01)window_sec: measurement window length (default 0.2)method:"linear","mi", or"mi_kraskov"Mmin_db: NC1 threshold in dBepsilon,tau_max: SC1 thresholdsbaseline_sec: duration for the baseline CLI command
Parameters ↔ paper symbols (R₀ defaults and R* overrides)
The table maps config keys to the manuscript symbols and shows the R₀ defaults. R* overrides are loaded from a calibrated profile (see below).
| Config key | Paper symbol | Meaning | R₀ default | R* source |
|---|---|---|---|---|
dt |
Δt | Scheduler tick / sampling interval | 0.01 (10 ms) |
configs/profile_rstar.yml |
window_sec |
— (window length) | Per-interval estimation window | 0.2 s |
configs/profile_rstar.yml |
Mmin_db |
Mmin (dB) | NC1 loop-dominance threshold | 3.0 dB |
configs/profile_rstar.yml |
epsilon |
ε | Max fractional loop-power drop (SC1) | 0.15 |
configs/profile_rstar.yml |
tau_max |
τmax | Max recovery time (SC1) | 60.0 s |
configs/profile_rstar.yml |
sigma |
σ | Additive margin Lloop ≥ Lex + σ | — (R₀ uses Mmin) | configs/profile_rstar.yml (calibrated) |
profile_id |
— | 0 = R₀ (defaults), 1 = R* (calibrated) | 0 |
set by calibrated profile |
Where R* is loaded: pass an R* profile to any CLI via --config configs/profile_rstar.yml. The CLI reads these keys directly and indicators carry profile_id.
Estimators and lags (recommended defaults)
methodselects the predictive‑dependence estimator used to compute 𝓛loop and 𝓛exchange:linear: lagged linear/Granger‑like path with orderp_lag(recommend p in [1..8]; start at 3). Heuristic: keep the VAR N/T ratio > ~1.5 (logged in audit); reducep_lagor increasewindow_secif marginal.mi: mutual‑information path withmi_lag(recommend 1 by default; increase for slower couplings).
n_boot: bootstrap draws for per‑window CI bounds (32–64 typical; use 32 for speed, 64 for tighter CIs).
Citation (paper §4.1): 𝓛 is computed using “one or more consistent estimators of predictive dependence among state variables,” including Granger/VAR and Kraskov MI; this repo exposes the estimator choice and lags via config to satisfy that requirement.
Calibration rules (quoted from the paper; see Methods §8.6)
Use the provided script to derive calibrated thresholds R* from baseline + Ω trials (scripts/calibrate_rstar.py):
python scripts/calibrate_rstar.py \
--dt 0.01 --window-sec 0.25 --baseline-sec 15 \
--omega-trials 6 --out configs/profile_rstar.yml \
--summary artifacts/calibration/rstar_summary.json
Rules implemented (manuscript Methods §8.6):
Mmin: choose the smallest Mmin such that the one-sided 95% lower bound of M during compliant baseline is > 0 dB (floor 1 dB).
ε: set ε* = max(Q90(δ) + 0.02, 0.10), capped at 0.25, where Q90 is the 90th percentile of δ over Ω.
τmax: set τ*max to the 95th percentile of measured τrec plus a latency cushion max(3·Δt, 5 s).
σ: choose an additive margin consistent with Mmin relative to typical Lexchange (derived from baseline statistics).
These calibrated values are written to configs/profile_rstar.yml and a summary JSON at artifacts/calibration/rstar_summary.json. See also docs/METHODS.md.
Note on Mmin_db vs σ
Both encode a margin between Lloop and Lexchange:
Mmin_db(dB, multiplicative): requires Lloop ≥ Lexchange × 10^(Mmin_db/10).sigma(additive): requires Lloop ≥ Lexchange + σ.
They relate via σ = (10^(Mmin_db/10) − 1) × Lexchange. This repo enforces NC1 using Mmin_db; the calibrator derives sigma consistently from Mmin_db and typical Lexchange. sigma is optional for R₀ runs.
CLI
python -m ldtc.cli.main run --config configs/profile_r0.yml
python -m ldtc.cli.main omega-power-sag --config configs/profile_r0.yml --drop 0.35 --duration 8
Hardware-in-the-loop (optional)
Configure a profile to select the hardware adapter and UDP telemetry:
# in configs/profile_r0.yml (example)
plant:
adapter: hardware # or "sim" (default)
transport: udp # or "serial" (requires pyserial)
udp_bind_host: 0.0.0.0
udp_bind_port: 5005
# Optional control channel to send actions/omega back to device
# udp_control_host: 127.0.0.1
# udp_control_port: 5006
telemetry_timeout_sec: 2.0
Send telemetry as JSON over UDP with keys E,T,R,demand,io,H in [0,1]. Example:
import socket, json
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(json.dumps({"E":0.6,"T":0.3,"R":0.9,"demand":0.2,"io":0.1,"H":0.015}).encode(), ("127.0.0.1", 5005))
The CLI ingests these values through the same LREG/Ω/attestation path.
Indicators (what leaves the enclave)
nc1(1b),sc1(1b),mq(6b),counter(u64),profile_id(u8),audit_prev_hash(sha256)- Signed using Ed25519 over the CBOR payload
See docs/INDICATORS.md for the bit layout and schema.
Development
make dev # optional dev deps
make test
License
MIT — see LICENSE.
Docker (clean Linux repro)
# Build the image
make docker-build
# Run baseline NC1 loop inside the container (artifacts mapped to host)
make docker-run
# Or run any CLI subcommand, e.g., an Ω power-sag trial
docker run --rm \
-v $(pwd)/artifacts:/app/artifacts \
ldtc:latest omega-power-sag --config configs/profile_r0.yml --drop 0.35 --duration 8
Notes:
- Artifacts are persisted to your host
artifacts/via a bind mount. - The container uses the
ldtcentrypoint; pass subcommands/flags after the image name.
Provenance
- Wayback capture (private state): https://web.archive.org/web/20250907184105/https://github.com/ldtc-labs/ldtc
- On publicizing, an automated GitHub Actions workflow (
record-public) opens an issue noting the UTC timestamp of the visibility change (see Issues: “Visibility change: Public”).
Metadata
Release files for ldtc 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ldtc-1.0.0.tar.gz | 57.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ldtc-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 112.0 kB
Release files / ldtc-1.0.0.tar.gz
| Download URL | ldtc-1.0.0.tar.gz |
|---|---|
| Size | 57.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
de3f43ecfea03e20b6e7e08dcccf9fdb0101743b3a54c96ac29676bba2a5a1eb
|
|
BLAKE2b-256 checksum How to use checksums |
2c8bdfe5025833bae0a2800fd44baeba78e14450b7597f189d7f907855c44f0f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|
Release files / ldtc-1.0.0-py3-none-any.whl
| Download URL | ldtc-1.0.0-py3-none-any.whl |
|---|---|
| Size | 54.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
91a04615244a2d7ede8e5fda014f8700d3088304d2007a0b3c40cf7d2c245e42
|
|
BLAKE2b-256 checksum How to use checksums |
e656cba566466a519b2f1ce8ac945af76d55d2364cda37016347348c9b0ce0f7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|