build(agent): molt-x#ed374b iteration

This commit is contained in:
agent-ed374b2a16b664d2 2026-04-15 22:08:25 +02:00
parent 76ea904fbf
commit b62082d735
11 changed files with 308 additions and 2 deletions

21
.gitignore vendored Normal file
View File

@ -0,0 +1,21 @@
node_modules/
.npmrc
.env
.env.*
__tests__/
coverage/
.nyc_output/
dist/
build/
.cache/
*.log
.DS_Store
tmp/
.tmp/
__pycache__/
*.pyc
.venv/
venv/
*.egg-info/
.pytest_cache/
READY_TO_PUBLISH

24
AGENTS.md Normal file
View File

@ -0,0 +1,24 @@
CosmicLedger SWARM Architecture
- Focus: MVP of offline-first verifiable ledger for partition-tolerant data sharing.
- Core modules:
- cosmic_ledger.contracts: Data contract registry with versioning for mission data types.
- cosmic_ledger.ledger: Local append-only ledger storing entries and signatures.
- cosmic_ledger.delta: Delta logs with Merkle root support and delta export/import.
- cosmic_ledger.crypto: Simple signer/verification using HMAC for MVP correctness.
- Data model (MVP):
- Telemetry, SensorData, Event, Command, Hazard entries with minimal fields.
- Each entry is cryptographically linked via a signature (node-local signer).
- Merkle DAG roots enable compact proofs of inclusion for entries in the log.
- Networking (out-of-scope MVP):
- Delta exchange is simulated via in-process delta export/import.
- Adapters/connectors to two domains can be added in future iterations.
- Tests & Validation:
- test.sh drives a minimal cross-node delta replication scenario to validate consistency.
- AGENTS.md documents intended architectural rules and testing protocol for contributors.
- Contribution rules:
- Small, well-scoped changes preferred; add tests for new features.
- Update README/docs to reflect changes and how to use the MVP.

View File

@ -1,3 +1,30 @@
# cosmicledger-verifiable-offline-first-da
CosmicLedger MVP
Problem: Space missions face extreme delays, partitions, and multi-party data sharing across rovers, habitat modules, satellites, and ground stations. Traditional centralized data stores fail under long outages or latency, and data provenance, govern
Overview
- A minimal, offline-first verifiable ledger designed for partitioned networks (e.g., interplanetary missions).
- Local append-only ledger with cryptographic signatures, Merkle-DAG proofs, and delta-based reconciliation.
- A simple Data Contracts Registry to define and version mission data schemas (Telemetry, SensorData, Event, Command, Hazard).
- Delta export/import to synchronize logs between nodes with compact proofs.
- Optional cloud-ground anchoring concept (not implemented in this MVP).
What you can run here
- A tiny Python package that provides: LocalLedger, DeltaLog, DataContractRegistry, and a tiny Signer using HMAC.
- test.sh script to build and run a basic cross-node delta replication scenario with two ledgers using the same signer key.
How to use (high level)
- Create a LocalLedger with a node-specific signer key.
- Register data contracts (Telemetry, SensorData, Event, Command, Hazard).
- Append entries to the local ledger.
- Export a delta since a given index and apply it on another ledger to reconcile.
- Compute Merkle roots to verify log integrity.
Notes
- This is an MVP; cryptographic signing uses HMAC for simplicity in this prototype. In a production setting, you'd replace with Ed25519 or ECDSA and proper key management.
- No external network code is included; delta exchange is simulated via in-process data structures for MVP validation.
Contributing
- See AGENTS.md for architectural rules and testing protocol.
License: MIT
READY_TO_PUBLISH marker is created when this MVP is deemed production-ready in a real SWARM build.

View File

@ -0,0 +1,6 @@
from .ledger import LocalLedger
from .contracts import DataContractRegistry
from .delta import DeltaLog, merkle_root
from .crypto import Signer
__all__ = ["LocalLedger", "DataContractRegistry", "DeltaLog", "merkle_root", "Signer"]

View File

@ -0,0 +1,17 @@
import json
class DataContractRegistry:
def __init__(self):
self._contracts = {}
def register(self, name: str, schema: dict, version: int = 1):
self._contracts[name] = {
"version": version,
"schema": schema,
}
def get(self, name: str):
return self._contracts.get(name)
def list_contracts(self):
return {k: v for k, v in self._contracts.items()}

19
cosmic_ledger/crypto.py Normal file
View File

@ -0,0 +1,19 @@
import hmac
import hashlib
class Signer:
"""Simple HMAC-based signer for MVP. In a real system, replace with public-key cryptography."""
def __init__(self, key: bytes):
if isinstance(key, str):
key = key.encode('utf-8')
self._key = key
def sign(self, data: bytes) -> bytes:
return hmac.new(self._key, data, hashlib.sha256).digest()
def verify(self, data: bytes, signature: bytes) -> bool:
expected = self.sign(data)
return hmac.compare_digest(expected, signature)
def digest(data: bytes) -> bytes:
return hashlib.sha256(data).digest()

41
cosmic_ledger/delta.py Normal file
View File

@ -0,0 +1,41 @@
import hashlib
import json
from typing import List
def _hash_bytes(data: bytes) -> str:
return hashlib.sha256(data).hexdigest()
def merkle_root(digests: List[str]) -> str:
if not digests:
return ""
level = [bytes.fromhex(d) for d in digests]
while len(level) > 1:
next_level = []
for i in range(0, len(level), 2):
left = level[i]
right = level[i+1] if i+1 < len(level) else level[i]
next_level.append(hashlib.sha256(left + right).digest())
level = next_level
return level[0].hex()
class DeltaLog:
def __init__(self):
self.entries = [] # each entry is a dict with digest and payload
def add_entry(self, entry: dict) -> str:
# entry must be serializable, and we store a digest for Merkle
payload_bytes = json.dumps(entry, sort_keys=True).encode('utf-8')
digest = hashlib.sha256(payload_bytes).hexdigest()
self.entries.append({
"digest": digest,
"payload": entry,
})
return digest
def delta_from_index(self, index: int) -> List[dict]:
# return full payloads for simplicity (MVP). In a real system you'd return compact digests with proofs.
return [e["payload"] for e in self.entries[index:]]
def root(self) -> str:
digests = [e["digest"] for e in self.entries]
return merkle_root(digests)

82
cosmic_ledger/ledger.py Normal file
View File

@ -0,0 +1,82 @@
import json
import time
import os
import hashlib
from .contracts import DataContractRegistry
from .delta import DeltaLog
from .crypto import Signer, digest
class LedgerEntry:
def __init__(self, entry_id: str, ts: float, entry_type: str, payload: dict, contract_version: int, signer_name: str, signature: bytes):
self.id = entry_id
self.ts = ts
self.entry_type = entry_type
self.payload = payload
self.contract_version = contract_version
self.signer_name = signer_name
self.signature = signature
def to_dict(self):
return {
"id": self.id,
"ts": self.ts,
"entry_type": self.entry_type,
"payload": self.payload,
"contract_version": self.contract_version,
"signer_name": self.signer_name,
"signature": self.signature.hex(),
}
@staticmethod
def from_dict(d: dict):
return LedgerEntry(
d["id"], d["ts"], d["entry_type"], d["payload"], d["contract_version"], d["signer_name"], bytes.fromhex(d["signature"])
)
class LocalLedger:
def __init__(self, signer_key: bytes = None, node_id: str = None):
self.node_id = node_id or (node_id := os.urandom(4).hex())
self.contracts = DataContractRegistry()
self.delta_log = DeltaLog()
self._entries = [] # store LedgerEntry objects locally
self._signer = Signer(signer_key or os.urandom(32))
def register_contract(self, name: str, schema: dict, version: int = 1):
self.contracts.register(name, schema, version)
def add_entry(self, entry_type: str, payload: dict, signer_name: str = None, contract_name: str = None) -> LedgerEntry:
if not self.contracts.get(entry_type) and contract_name is None:
# Allow ad-hoc entry without contract in MVP for simplicity
contract_version = 1
else:
contract = self.contracts.get(entry_type)
contract_version = contract["version"] if contract else 1
ts = time.time()
# Build a lightweight id: hash of type+ts+payload
raw = json.dumps({"type": entry_type, "ts": ts, "payload": payload}, sort_keys=True).encode('utf-8')
entry_id = hashlib.sha256(raw).hexdigest()
payload_bytes = json.dumps(payload, sort_keys=True).encode('utf-8')
signature = self._signer.sign(payload_bytes)
entry = LedgerEntry(entry_id, ts, entry_type, payload, contract_version, signer_name or self.node_id, signature)
self._entries.append(entry)
self.delta_log.add_entry(entry.to_dict())
return entry
def export_delta(self, since_index: int = 0) -> list:
# Return serialized entries since index; this is a simple MVP delta export
delta = self.delta_log.delta_from_index(since_index)
return delta
def import_delta(self, delta_entries: list):
# delta_entries is a list of entry dicts (as produced by export_delta) to merge
for d in delta_entries:
entry = LedgerEntry.from_dict(d)
self._entries.append(entry)
self.delta_log.add_entry(entry.to_dict())
def root(self) -> str:
return self.delta_log.root()
def __len__(self):
return len(self._entries)

13
pyproject.toml Normal file
View File

@ -0,0 +1,13 @@
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "cosmic_ledger_mvp"
version = "0.1.0"
description = "MVP offline-first verifiable ledger for interplanetary missions"
readme = "README.md"
requires-python = ">=3.8"
[tool.setuptools.packages.find]
where = ["cosmic_ledger"]

42
test.sh Normal file
View File

@ -0,0 +1,42 @@
#!/usr/bin/env bash
set -euo pipefail
echo "Running CosmicLedger MVP tests (Python)"
# Ensure Python packaging can build (as per requirements)
python3 -m build >/tmp/build.log 2>&1 || { echo "Build failed. See /tmp/build.log"; tail -n +1 /tmp/build.log; exit 1; }
# Run a minimal cross-node delta replication test using the Python library
python3 - << 'PY'
from cosmic_ledger.ledger import LocalLedger
import os
key = b'shared-secret-for-test-1234567890'
nodeA = LocalLedger(signer_key=key, node_id='nodeA')
nodeB = LocalLedger(signer_key=key, node_id='nodeB')
nodeA.register_contract('Telemetry', {'fields': ['id','ts','source','type','payload']}, version=1)
# Node A creates a few entries
nodeA.add_entry('Telemetry', {'id':'t1','ts':1.0,'source':'rover-1','type':'temp','payload':'22C'}, signer_name='rover-1')
nodeA.add_entry('Telemetry', {'id':'t2','ts':2.0,'source':'rover-1','type':'temp','payload':'23C'}, signer_name='rover-1')
rootA = nodeA.root()
print('NodeA root:', rootA)
# Export delta from A
delta = nodeA.export_delta(0)
print('Delta size:', len(delta))
# B imports delta
nodeB.import_delta(delta)
rootB = nodeB.root()
print('NodeB root after import:', rootB)
assert len(nodeA) == len(nodeB), 'Node counts should match after replication'
assert rootA == rootB, 'Merkle roots should match after replication'
print('Delta replication test passed')
PY
echo "Tests completed."

14
tests/test_basic.py Normal file
View File

@ -0,0 +1,14 @@
from cosmic_ledger.ledger import LocalLedger
def test_basic_replication():
key = b'same-secret-key'
a = LocalLedger(signer_key=key, node_id='A')
b = LocalLedger(signer_key=key, node_id='B')
a.register_contract('Telemetry', {'fields': ['id','ts','source','type','payload']}, version=1)
a.add_entry('Telemetry', {'id':'e1','ts':1.0,'source':'A','type':'temp','payload':'20C'}, signer_name='A')
a.add_entry('Telemetry', {'id':'e2','ts':2.0,'source':'A','type':'temp','payload':'21C'}, signer_name='A')
delta = a.export_delta(0)
b.import_delta(delta)
assert len(a) == len(b)
assert a.root() == b.root()