Files
breakpilot-compliance/backend-compliance/compliance/services/dsms_client.py
T
Benjamin Admin 66d30568e2
Build + Deploy / build-admin-compliance (push) Successful in 1m41s
Build + Deploy / build-backend-compliance (push) Successful in 14s
Build + Deploy / build-ai-sdk (push) Successful in 41s
Build + Deploy / build-developer-portal (push) Successful in 10s
Build + Deploy / build-tts (push) Successful in 10s
Build + Deploy / build-document-crawler (push) Successful in 10s
Build + Deploy / build-dsms-gateway (push) Successful in 10s
Build + Deploy / build-dsms-node (push) Successful in 11s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / loc-budget (push) Failing after 14s
CI / secret-scan (push) Has been skipped
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Successful in 2m31s
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / test-go (push) Successful in 48s
CI / test-python-backend (push) Failing after 1s
CI / test-python-document-crawler (push) Successful in 32s
CI / test-python-dsms-gateway (push) Successful in 25s
CI / validate-canonical-controls (push) Successful in 15s
Build + Deploy / trigger-orca (push) Successful in 2m23s
feat(dsms): Stufe 1 — Gap-Analyse Report wird in DSMS archiviert
- Go DSMS Client (internal/dsms/client.go): Archive() + Verify()
- Python DSMS Client (compliance/services/dsms_client.py): archive_to_dsms() + verify_dsms()
- Gap-Analyse AnalyzeProject() archiviert Report-JSON nach DSMS
- Response enthält dsms_cid wenn Archivierung erfolgreich
- Frontend: Grünes "Revisionssicher archiviert" Badge mit CID im GapDashboard
- DSMS Proxy Route (/api/sdk/v1/dsms/[...path]) für Verify-Abfragen

Stufe 2 (Evidence Upload → DSMS) und Stufe 3 (Version Chains) folgen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-11 23:39:26 +02:00

92 lines
2.6 KiB
Python

"""DSMS (Dezentrales Daten Speicher System) Client — archives compliance
artifacts to IPFS via the dsms-gateway for tamper-proof versioning.
Usage:
from compliance.services.dsms_client import archive_to_dsms, verify_dsms
result = await archive_to_dsms(
content=pdf_bytes,
filename="gap-report-2026-05-11.json",
document_type="gap_report",
document_id=str(report_id),
version="1",
)
cid = result["cid"] # IPFS Content Identifier
"""
import logging
import os
import httpx
logger = logging.getLogger(__name__)
DSMS_GATEWAY_URL = os.environ.get("DSMS_GATEWAY_URL", "http://dsms-gateway:8082")
async def archive_to_dsms(
content: bytes,
filename: str,
document_type: str,
document_id: str,
version: str = "1",
parent_cid: str | None = None,
language: str = "de",
tenant_id: str | None = None,
) -> dict:
"""Archive binary content to DSMS (IPFS).
Returns dict with keys: cid, size, gateway_url.
Returns empty dict on failure (non-critical — logs warning).
"""
data = {
"document_type": document_type,
"document_id": document_id,
"version": version,
"language": language,
}
if parent_cid:
data["parent_cid"] = parent_cid
if tenant_id:
data["tenant_id"] = tenant_id
try:
async with httpx.AsyncClient(timeout=60.0) as client:
resp = await client.post(
f"{DSMS_GATEWAY_URL}/api/v1/documents",
files={"file": (filename, content)},
data=data,
headers={"Authorization": "Bearer system-backend"},
)
if resp.status_code != 200:
logger.warning("DSMS archive failed (%d): %s", resp.status_code, resp.text[:200])
return {}
result = resp.json()
logger.info("DSMS archived: %s → CID %s (%d bytes)", filename, result.get("cid", "?"), result.get("size", 0))
return result
except Exception as e:
logger.warning("DSMS archive unavailable: %s", e)
return {}
async def verify_dsms(cid: str) -> dict:
"""Verify a document's integrity by CID.
Returns dict with verification result or empty dict on failure.
"""
try:
async with httpx.AsyncClient(timeout=15.0) as client:
resp = await client.get(f"{DSMS_GATEWAY_URL}/api/v1/verify/{cid}")
if resp.status_code != 200:
return {"valid": False, "error": f"HTTP {resp.status_code}"}
return resp.json()
except Exception as e:
logger.warning("DSMS verify unavailable: %s", e)
return {"valid": False, "error": str(e)}