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
- 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>
92 lines
2.6 KiB
Python
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)}
|