Single-machine, real-time digital boundary organism: NC1/SC1 verification harness.
Project description
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”).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ldtc-1.0.0.tar.gz.
File metadata
- Download URL: ldtc-1.0.0.tar.gz
- Upload date:
- Size: 57.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
de3f43ecfea03e20b6e7e08dcccf9fdb0101743b3a54c96ac29676bba2a5a1eb
|
|
| MD5 |
ed654422c194cb2306dcf5e36b0a16a8
|
|
| BLAKE2b-256 |
2c8bdfe5025833bae0a2800fd44baeba78e14450b7597f189d7f907855c44f0f
|
File details
Details for the file ldtc-1.0.0-py3-none-any.whl.
File metadata
- Download URL: ldtc-1.0.0-py3-none-any.whl
- Upload date:
- Size: 54.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
91a04615244a2d7ede8e5fda014f8700d3088304d2007a0b3c40cf7d2c245e42
|
|
| MD5 |
4a3afb342cf988e0e771b49e0687b78b
|
|
| BLAKE2b-256 |
e656cba566466a519b2f1ce8ac945af76d55d2364cda37016347348c9b0ce0f7
|