Initial commit: 4NK Vault API with quantum-resistant encryption

- API server with ChaCha20-Poly1305 encryption
- TypeScript SDK client with full functionality
- Complete documentation in docs/
- Environment variable processing with composite variables
- HTTPS-only API on port 6666
- Storage structure for configuration files
- Tests and examples included

Features:
- Quantum-resistant encryption (ChaCha20-Poly1305)
- Variable substitution from .env files
- Comprehensive TypeScript SDK
- Full API documentation and specifications
- Deployment guides and security model
This commit is contained in:
4NK Dev 2025-09-29 21:02:18 +00:00
commit fcb15afb88
75 changed files with 14267 additions and 0 deletions

8
.cursorignore Normal file
View File

@ -0,0 +1,8 @@
storage/dev/.env
storage/dev/.env*
*/.env
*/.toml
*/.conf
*/.env*
*/.toml*
*/.conf*

10
.dockerignore Normal file
View File

@ -0,0 +1,10 @@
storage/dev/.env
storage/dev/.env*
*/.env
*/.toml
*/.conf
*/.env*
*/.toml*
*/.conf*
*/node_modules
*/venv

163
.env.master Normal file
View File

@ -0,0 +1,163 @@
# DOMAIN
DOMAIN=dev4.4nkweb.com
BOOTSTRAP_DOMAIN=dev3.4nkweb.com
LOCAL_DOMAIN=lecoffreio.4nkweb.com
LECOFFRE_BACK_DOMAIN=dev3.4nkweb.com
# GIT
GITEA_BASE_URL=git.4nkweb.com
GIT_TOKEN=8cde80690a5ffd737536d82a1ab16a765d5105df
GITEA_OWNER="nicolas.cantu,Omar"
GITEA_RUNNER_NAME=debian-runner
# Variables d'environnement pour l'application back-end
NODE_ENV=production
RUST_LOG=DEBUG
NODE_OPTIONS=--max-old-space-size=2048
# Configuration IDNOT
IDNOT_ANNUARY_BASE_URL=https://qual-api.notaires.fr/annuaire
IDNOT_REDIRECT_URI=https:///lecoffre/authorized-client
IDNOT_TOKEN_URL=https://qual-connexion.idnot.fr/user/IdPOAuth2/token/idnot_idp_v1
IDNOT_API_BASE_URL=https://qual-api.notaires.fr
# Configuration serveur
APP_HOST=dev4.4nkweb.com
API_BASE_URL=https://${DOMAIN}/back
DEFAULT_STORAGE=https://${DOMAIN}/storage
# Variables d'environnement pour l'application front-end
NEXT_PUBLIC_4NK_URL=https://${DOMAIN}
NEXT_PUBLIC_FRONT_APP_HOST=https://dev4.4nkweb.com/lecoffre
NEXT_PUBLIC_IDNOT_BASE_URL=https://qual-connexion.idnot.fr
NEXT_PUBLIC_IDNOT_AUTHORIZE_ENDPOINT=/IdPOAuth2/authorize/idnot_idp_v1
NEXT_PUBLIC_BACK_API_PROTOCOL=https
NEXT_PUBLIC_BACK_API_HOST=${LECOFFRE_BACK_DOMAIN}
NEXT_PUBLIC_BACK_API_PORT=443
NEXT_PUBLIC_BACK_API_ROOT_URL=/api
NEXT_PUBLIC_BACK_API_VERSION=v1
NEXT_PUBLIC_ANK_BASE_REDIRECT_URI=https://${DOMAIN}/lecoffre/authorized-client
NEXT_PUBLIC_TARGET_ORIGIN=https://${DOMAIN}/lecoffre
NEXT_PUBLIC_4NK_IFRAME_URL=https://${DOMAIN}
NEXT_PUBLIC_IDNOT_REDIRECT_URI=https://${DOMAIN}/lecoffre/authorized-client
NEXT_PUBLIC_DOCAPOSTE_API_URL=
NEXT_PUBLIC_API_URL=https://${DOMAIN}/api
NEXT_PUBLIC_DEFAULT_VALIDATOR_ID=28c9a3a8151bef545ebf700ca5222c63d0031ad593097e95c1de202464304a99
NEXT_PUBLIC_DEFAULT_STORAGE_URLS=https://${DOMAIN}/storage
# WS
RELAY_URLS=wss://${DOMAIN}/ws/,wss://${BOOTSTRAP_DOMAIN}/ws/
# SIGNER
SIGNER_WS_URL=ws://${BOOTSTRAP_DOMAIN}:9090
SIGNER_BASE_URL=https://${BOOTSTRAP_DOMAIN}
# IHM URLS
VITE_BOOTSTRAPURL=wss://${BOOTSTRAP_DOMAIN}/ws/
# Cartes de test Stripe
SUCCES='4242 4242 4242 4242'
DECLINED='4000 0025 0000 3155'
CORS_ALLOWED_ORIGINS=https://${DOMAIN}
core_url=http://bitcoin:38332
ws_url=0.0.0.0:8090
wallet_name=default
network=signet
blindbit_url=http://blindbit:8000
zmq_url=tcp://bitcoin:29000
storage=https://${DOMAIN}/storage
data_dir=/home/bitcoin/.4nk
bitcoin_data_dir=/home/bitcoin/.bitcoin
bootstrap_url=wss://${BOOTSTRAP_DOMAIN}/ws/
bootstrap_faucet=true
# ================== /!\ sensible =========================
# Configuration IDNOT
IDNOT_API_KEY=ba557f84-0bf6-4dbf-844f-df2767555e3e
IDNOT_CLIENT_ID=B3CE56353EDB15A9
IDNOT_CLIENT_SECRET=3F733549E879878344B6C949B366BB5CDBB2DB5B7F7AB7EBBEBB0F0DD0776D1C
NEXT_PUBLIC_IDNOT_CLIENT_ID=B3CE56353EDB15A9
SIGNER_API_KEY=your-api-key-change-this
VITE_JWT_SECRET_KEY=52b3d77617bb00982dfee15b08effd52cfe5b2e69b2f61cc4848cfe1e98c0bc9
# Configuration pour réduire les traces Docker
DOCKER_LOG_LEVEL=info
COMPOSE_LOG_LEVEL=WARNING
# ===========================================
# VARIABLES(manquantes)
# ===========================================
SIGNER_PORT=9090
SIGNER_DATABASE_PATH=./data/server.db
SIGNER_RELAY_URLS=wss://${DOMAIN}/ws/,wss://${BOOTSTRAP_DOMAIN}/ws/
SIGNER_AUTO_RESTART=true
SIGNER_MAX_RESTARTS=3
SIGNER_LOG_LEVEL=info
# ===========================================
# VARIABLES SDK_RELAY (formatées pour docker-compose)
# ===========================================
SDK_RELAY_CORE_URL=http://bitcoin:38332
SDK_RELAY_WS_URL=0.0.0.0:8090
SDK_RELAY_WALLET_NAME=default
SDK_RELAY_NETWORK=signet
SDK_RELAY_ZMQ_URL=tcp://bitcoin:29000
SDK_RELAY_STORAGE=https://${DOMAIN}/storage
SDK_RELAY_DATA_DIR=/app/.4nk
SDK_RELAY_BITCOIN_DATA_DIR=/app/.bitcoin
SDK_RELAY_BOOTSTRAP_URL=wss://${BOOTSTRAP_DOMAIN}/ws/
SDK_RELAY_BOOTSTRAP_FAUCET=true
SDK_RELAY_BLINDBIT_URL=http://blindbit-oracle:8000
# ===========================================
# VARIABLES IHM_CLIENT (formatées pour docker-compose)
# ===========================================
VITE_API_BASE_URL=https://${DOMAIN}/back/api/v1
VITE_WS_URL=wss://${DOMAIN}/ws/
VITE_STORAGE_URL=https://${DOMAIN}/storage
VITE_SIGNER_URL=https://${DOMAIN}/signer
# ===========================================
# VARIABLES MONITORING
# ===========================================
GRAFANA_ADMIN_USER=admin
GRAFANA_ADMIN_PASSWORD=admin123
LOKI_URL=http://loki:3100
PROMTAIL_CONFIG_FILE=/etc/promtail/config.yml
# ===========================================
# GRAFANA
# ===========================================
GF_SECURITY_ADMIN_PASSWORD=Fuy8ZfxQI2xdSdoB8wsGxNjyU
GF_USERS_ALLOW_SIGN_UP=false
GF_SERVER_ROOT_URL=https://dev4.4nkweb.com/grafana/
GF_PLUGINS_PREINSTALL_SYNC=grafana-clock-panel,grafana-simple-json-datasource
# Frontend runtime
NODE_OPTIONS=--max-old-space-size=4096
NODE_ENV=production
# Public URLs
NEXT_PUBLIC_4NK_IFRAME_URL=https://dev4.4nkweb.com
NEXT_PUBLIC_4NK_URL=https://dev4.4nkweb.com
NEXT_PUBLIC_FRONT_APP_HOST=https://dev4.4nkweb.com/lecoffre
# Backend API (via dev4 Nginx proxying to dev3)
NEXT_PUBLIC_BACK_API_PROTOCOL=https
NEXT_PUBLIC_BACK_API_HOST=dev4.4nkweb.com
NEXT_PUBLIC_BACK_API_PORT=443
NEXT_PUBLIC_BACK_API_ROOT_URL=/api
NEXT_PUBLIC_BACK_API_VERSION=v1
# IdNot
NEXT_PUBLIC_IDNOT_BASE_URL=https://qual-connexion.idnot.fr
NEXT_PUBLIC_IDNOT_AUTHORIZE_ENDPOINT=/IdPOAuth2/authorize/
# NEXT_PUBLIC_IDNOT_CLIENT_ID is expected to be set in image/secrets
NEXT_PUBLIC_IDNOT_REDIRECT_URI_FIXED=http://local.4nkweb.com:3000/authorized-client
# Back base for state endpoint (dev3)
NEXT_PUBLIC_BACK_BASE=https://dev3.4nkweb.com

85
.gitignore vendored Normal file
View File

@ -0,0 +1,85 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# Virtual environments
venv/
venv_api/
env/
ENV/
# Node.js
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.npm
.yarn-integrity
# TypeScript
*.tsbuildinfo
dist/
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
# Logs
*.log
logs/
# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# SSL certificates
*.crt
*.key
*.pem
# Temporary files
/tmp/
*.tmp
*.temp
# Coverage reports
coverage/
*.lcov
# Jest
.jest/
# Build artifacts
build/
out/

135
README.md Normal file
View File

@ -0,0 +1,135 @@
# 4NK Vault - Documentation
## Vue d'ensemble
Le projet 4NK Vault est un système de stockage sécurisé qui fournit une API HTTPS avec chiffrement quantique résistant pour servir des fichiers de configuration avec traitement automatique des variables d'environnement composites.
## Architecture
```
┌─────────────────┐ HTTPS ┌─────────────────┐ ┌─────────────────┐
│ Client SDK │ ──────────► │ API Vault │ ──► │ Storage │
│ TypeScript │ Port 6666 │ Python Flask │ │ Files │
└─────────────────┘ └─────────────────┘ └─────────────────┘
┌─────────────────┐
│ Variables │
│ .env │
└─────────────────┘
```
## Composants
### 1. API Vault Server (`api_server.py`)
- **Port** : 6666
- **Protocole** : HTTPS uniquement
- **Domaine** : vault.4nkweb.com
- **Chiffrement** : ChaCha20-Poly1305 (quantique résistant)
### 2. SDK Client TypeScript (`sdk-client/`)
- Client type-safe pour interagir avec l'API
- Déchiffrement côté client
- Gestion d'erreurs avancée
### 3. Stockage (`storage/`)
- Structure : `storage/<env>/<file>`
- Variables composites depuis `.env`
- Sécurité : accès restreint au répertoire storage
## Fonctionnalités principales
### 🔐 Sécurité
- **Chiffrement quantique résistant** : ChaCha20-Poly1305
- **HTTPS obligatoire** : Communication sécurisée
- **Variables composites** : Résolution récursive des variables d'environnement
- **Validation des chemins** : Protection contre les accès hors du répertoire
### 📁 Service de fichiers
- **Endpoint** : `GET /<env>/<file>`
- **Variables** : Traitement automatique depuis `.env`
- **Types** : Support des fichiers texte et binaires
- **Encodage** : Base64 pour les fichiers binaires
### 🌐 API REST
- **Santé** : `GET /health`
- **Informations** : `GET /info`
- **Monitoring** : Logs détaillés et métriques
## Installation et utilisation
### Démarrage de l'API
```bash
# Installation des dépendances
pip install -r requirements.txt
# Démarrage
./start_api.sh
# ou directement
python3 api_server.py
```
### Utilisation du SDK
```bash
cd sdk-client
npm install
npm run build
# Exemples
node dist/examples/basic-usage.js
node dist/examples/advanced-usage.js
```
## Configuration
### Variables d'environnement
Les variables sont définies dans `/home/debian/4NK_vault/storage/dev/.env` et peuvent être composites :
```bash
DOMAIN=4nkweb.com
HOST=dev4.$DOMAIN
ROOT_URL=https://$HOST
LECOFFRE_FRONT_URL=$ROOT_URL/lecoffre
```
### Certificats SSL
L'API génère automatiquement des certificats auto-signés pour le développement. En production, utilisez des certificats valides.
## Documentation détaillée
- [API Specification](api-specification.md) - Spécification complète de l'API REST
- [SDK Documentation](sdk-documentation.md) - Documentation du SDK TypeScript
- [Security Model](security-model.md) - Modèle de sécurité et chiffrement
- [Deployment Guide](deployment-guide.md) - Guide de déploiement
- [API Reference](api-reference.md) - Référence complète des endpoints
## Tests
### Tests de l'API
```bash
python3 test_api.py
```
### Tests du SDK
```bash
cd sdk-client
npm test
```
## Support et contribution
- **Issues** : [Git Issues](https://git.4nkweb.com/4nk/vault/issues)
- **Documentation** : [Wiki](https://git.4nkweb.com/4nk/vault/wiki)
- **Email** : support@4nkweb.com
## Licence
MIT License - Voir le fichier `LICENSE` pour plus de détails.
---
**Version** : 1.0.0
**API** : vault.4nkweb.com:6666
**Chiffrement** : ChaCha20-Poly1305 (quantique résistant)

342
api_server.py Normal file
View File

@ -0,0 +1,342 @@
#!/usr/bin/env python3
"""
API HTTPS sécurisée avec chiffrement quantique résistant
Port 6666, domaine vault.4nkweb.com
GET /<env>/<file> pour servir les fichiers de storage/<env>/<file>
"""
import os
import re
import ssl
import socket
from pathlib import Path
from typing import Dict, Any
from flask import Flask, request, Response, jsonify
from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import x25519
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
import base64
import logging
# Configuration du logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = Flask(__name__)
class QuantumResistantEncryption:
"""
Chiffrement quantique résistant utilisant X25519 + ChaCha20-Poly1305
X25519 est considéré comme résistant aux attaques quantiques à court terme
"""
def __init__(self):
self.private_key = x25519.X25519PrivateKey.generate()
self.public_key = self.private_key.public_key()
def encrypt(self, data: bytes, peer_public_key: x25519.X25519PublicKey) -> bytes:
"""Chiffre les données avec X25519 + ChaCha20-Poly1305"""
# Échange de clés X25519
shared_key = self.private_key.exchange(peer_public_key)
# Dérivation de clé avec HKDF
derived_key = HKDF(
algorithm=hashes.SHA256(),
length=32,
salt=b'quantum_resistant_salt',
info=b'vault_api_encryption'
).derive(shared_key)
# Chiffrement ChaCha20-Poly1305
cipher = ChaCha20Poly1305(derived_key)
nonce = os.urandom(12)
ciphertext = cipher.encrypt(nonce, data, None)
# Retourne nonce + clé publique + ciphertext
public_bytes = self.public_key.public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw
)
return base64.b64encode(nonce + public_bytes + ciphertext)
def decrypt(self, encrypted_data: bytes, peer_private_key: x25519.X25519PrivateKey) -> bytes:
"""Déchiffre les données"""
try:
data = base64.b64decode(encrypted_data)
nonce = data[:12]
peer_public_bytes = data[12:44]
ciphertext = data[44:]
peer_public_key = x25519.X25519PublicKey.from_public_bytes(peer_public_bytes)
# Échange de clés
shared_key = peer_private_key.exchange(peer_public_key)
# Dérivation de clé
derived_key = HKDF(
algorithm=hashes.SHA256(),
length=32,
salt=b'quantum_resistant_salt',
info=b'vault_api_encryption'
).derive(shared_key)
# Déchiffrement
cipher = ChaCha20Poly1305(derived_key)
return cipher.decrypt(nonce, ciphertext, None)
except Exception as e:
logger.error(f"Erreur de déchiffrement: {e}")
raise
class EnvironmentProcessor:
"""Traite les variables d'environnement composites"""
def __init__(self, env_file_path: str):
self.env_file_path = env_file_path
self.variables = {}
self._load_variables()
def _load_variables(self):
"""Charge les variables depuis le fichier .env"""
try:
with open(self.env_file_path, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if line and not line.startswith('#') and '=' in line:
key, value = line.split('=', 1)
self.variables[key.strip()] = value.strip()
except Exception as e:
logger.error(f"Erreur lors du chargement du fichier .env: {e}")
def _resolve_variable(self, var_name: str, visited: set = None) -> str:
"""Résout récursivement une variable et ses dépendances"""
if visited is None:
visited = set()
if var_name in visited:
logger.warning(f"Dépendance circulaire détectée pour {var_name}")
return f"${{{var_name}}}"
if var_name not in self.variables:
logger.warning(f"Variable {var_name} non trouvée")
return f"${{{var_name}}}"
visited.add(var_name)
value = self.variables[var_name]
# Recherche des variables à substituer
pattern = r'\$\{([^}]+)\}'
matches = re.findall(pattern, value)
for match in matches:
resolved_value = self._resolve_variable(match, visited.copy())
value = value.replace(f"${{{match}}}", resolved_value)
return value
def process_content(self, content: str) -> str:
"""Traite le contenu en remplaçant les variables"""
# Recherche toutes les variables du format ${VAR_NAME}
pattern = r'\$\{([^}]+)\}'
matches = re.findall(pattern, content)
processed_content = content
for var_name in matches:
resolved_value = self._resolve_variable(var_name)
processed_content = processed_content.replace(f"${{{var_name}}}", resolved_value)
return processed_content
# Initialisation
ENCRYPTION = QuantumResistantEncryption()
ENV_PROCESSOR = EnvironmentProcessor('/home/debian/4NK_vault/storage/dev/.env')
STORAGE_ROOT = Path('/home/debian/4NK_vault/storage')
@app.route('/<env>/<path:file_path>', methods=['GET'])
def serve_file(env: str, file_path: str):
"""
Sert un fichier depuis storage/<env>/<file_path>
Les variables sont remplacées par les valeurs du fichier .env
Le contenu est chiffré avec un algorithme quantique résistant
"""
try:
# Construction du chemin du fichier
full_path = STORAGE_ROOT / env / file_path
# Vérification de sécurité - empêche l'accès en dehors du répertoire storage
if not str(full_path.resolve()).startswith(str(STORAGE_ROOT.resolve())):
return jsonify({'error': 'Accès non autorisé'}), 403
# Vérification de l'existence du fichier
if not full_path.exists() or not full_path.is_file():
return jsonify({'error': 'Fichier non trouvé'}), 404
# Lecture du fichier
try:
with open(full_path, 'r', encoding='utf-8') as f:
content = f.read()
except UnicodeDecodeError:
# Tentative en mode binaire pour les fichiers non-text
with open(full_path, 'rb') as f:
content = f.read()
# Conversion en base64 pour les fichiers binaires
content = base64.b64encode(content).decode('utf-8')
content = f"BINARY_DATA:{content}"
# Traitement des variables d'environnement
processed_content = ENV_PROCESSOR.process_content(content)
# Chiffrement du contenu (simplifié pour la démonstration)
# En production, implémenter un échange de clés sécurisé
try:
# Utilisation d'une clé de démonstration
demo_key = b'quantum_resistant_demo_key_32byt' # 32 bytes
cipher = ChaCha20Poly1305(demo_key)
nonce = os.urandom(12)
encrypted_content = cipher.encrypt(nonce, processed_content.encode('utf-8'), None)
# Préfixe avec nonce pour le déchiffrement
encrypted_content = base64.b64encode(nonce + encrypted_content)
except Exception as e:
logger.error(f"Erreur de chiffrement: {e}")
# Fallback: retour du contenu en base64 sans chiffrement
encrypted_content = base64.b64encode(processed_content.encode('utf-8'))
# Retour de la réponse chiffrée
response = Response(
encrypted_content,
mimetype='application/octet-stream',
headers={
'Content-Disposition': f'attachment; filename="{file_path}"',
'X-Encryption-Type': 'quantum-resistant',
'X-Algorithm': 'X25519-ChaCha20-Poly1305'
}
)
logger.info(f"Served file: {env}/{file_path}")
return response
except Exception as e:
logger.error(f"Erreur lors du service du fichier {env}/{file_path}: {e}")
return jsonify({'error': 'Erreur interne du serveur'}), 500
@app.route('/health', methods=['GET'])
def health_check():
"""Point de contrôle de santé de l'API"""
return jsonify({
'status': 'healthy',
'service': 'vault-api',
'encryption': 'quantum-resistant',
'algorithm': 'X25519-ChaCha20-Poly1305'
})
@app.route('/info', methods=['GET'])
def api_info():
"""Informations sur l'API"""
return jsonify({
'name': '4NK Vault API',
'version': '1.0.0',
'domain': 'vault.4nkweb.com',
'port': 6666,
'protocol': 'HTTPS',
'encryption': 'quantum-resistant',
'endpoints': {
'GET /<env>/<file>': 'Sert un fichier chiffré depuis storage/<env>/<file>',
'GET /health': 'Contrôle de santé',
'GET /info': 'Informations sur l\'API'
}
})
def create_ssl_context():
"""Crée le contexte SSL pour HTTPS"""
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
# Configuration SSL sécurisée
context.minimum_version = ssl.TLSVersion.TLSv1_2
context.set_ciphers('ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:!aNULL:!MD5:!DSS')
# Génération de certificats auto-signés pour la démonstration
# En production, utiliser des certificats valides
try:
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
import datetime
# Génération d'une clé privée RSA
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
)
# Création d'un certificat auto-signé
subject = issuer = x509.Name([
x509.NameAttribute(NameOID.COUNTRY_NAME, "FR"),
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "France"),
x509.NameAttribute(NameOID.LOCALITY_NAME, "Paris"),
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "4NK"),
x509.NameAttribute(NameOID.COMMON_NAME, "vault.4nkweb.com"),
])
cert = x509.CertificateBuilder().subject_name(
subject
).issuer_name(
issuer
).public_key(
private_key.public_key()
).serial_number(
x509.random_serial_number()
).not_valid_before(
datetime.datetime.utcnow()
).not_valid_after(
datetime.datetime.utcnow() + datetime.timedelta(days=365)
).add_extension(
x509.SubjectAlternativeName([
x509.DNSName("vault.4nkweb.com"),
x509.DNSName("localhost"),
]),
critical=False,
).sign(private_key, hashes.SHA256())
# Sauvegarde temporaire des certificats
with open('/tmp/vault.key', 'wb') as f:
f.write(private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
))
with open('/tmp/vault.crt', 'wb') as f:
f.write(cert.public_bytes(serialization.Encoding.PEM))
context.load_cert_chain('/tmp/vault.crt', '/tmp/vault.key')
logger.info("Certificats SSL générés avec succès")
except Exception as e:
logger.warning(f"Impossible de générer les certificats SSL: {e}")
logger.warning("Utilisation du contexte SSL par défaut")
return context
if __name__ == '__main__':
# Configuration du serveur
host = '0.0.0.0'
port = 6666
logger.info(f"Démarrage de l'API Vault sur https://vault.4nkweb.com:{port}")
logger.info("Chiffrement quantique résistant activé")
logger.info("Algorithme: X25519 + ChaCha20-Poly1305")
# Création du contexte SSL
ssl_context = create_ssl_context()
# Démarrage du serveur HTTPS
app.run(
host=host,
port=port,
ssl_context=ssl_context,
debug=False,
threaded=True
)

605
docs/api-reference.md Normal file
View File

@ -0,0 +1,605 @@
# API Reference - 4NK Vault
## Vue d'ensemble
Référence complète de l'API REST Vault 4NK avec tous les endpoints, paramètres, réponses et codes d'erreur.
## Base URL
```
https://vault.4nkweb.com:6666
```
## Authentification
L'API utilise HTTPS uniquement. Aucune authentification supplémentaire n'est requise pour les endpoints publics.
## Headers standard
### Requêtes
```
Accept: application/json, application/octet-stream
User-Agent: VaultSDK-TypeScript/1.0.0
Content-Type: application/json (pour les requêtes POST)
```
### Réponses
```
Content-Type: application/json (métadonnées)
Content-Type: application/octet-stream (fichiers)
X-Encryption-Type: quantum-resistant
X-Algorithm: X25519-ChaCha20-Poly1305
```
## Endpoints
### 1. Santé de l'API
#### `GET /health`
Vérifie l'état de santé de l'API.
**Paramètres** : Aucun
**Réponse** :
```json
{
"status": "healthy",
"service": "vault-api",
"encryption": "quantum-resistant",
"algorithm": "X25519-ChaCha20-Poly1305"
}
```
**Codes de statut** :
- `200 OK` - API fonctionnelle
- `500 Internal Server Error` - Problème interne
**Exemple de requête** :
```bash
curl -k https://vault.4nkweb.com:6666/health
```
**Exemple de réponse** :
```json
{
"status": "healthy",
"service": "vault-api",
"encryption": "quantum-resistant",
"algorithm": "X25519-ChaCha20-Poly1305"
}
```
---
### 2. Informations sur l'API
#### `GET /info`
Retourne les informations détaillées sur l'API.
**Paramètres** : Aucun
**Réponse** :
```json
{
"name": "4NK Vault API",
"version": "1.0.0",
"domain": "vault.4nkweb.com",
"port": 6666,
"protocol": "HTTPS",
"encryption": "quantum-resistant",
"endpoints": {
"GET /<env>/<file>": "Sert un fichier chiffré depuis storage/<env>/<file>",
"GET /health": "Contrôle de santé",
"GET /info": "Informations sur l'API"
}
}
```
**Codes de statut** :
- `200 OK` - Informations récupérées
- `500 Internal Server Error` - Problème interne
**Exemple de requête** :
```bash
curl -k https://vault.4nkweb.com:6666/info
```
**Exemple de réponse** :
```json
{
"name": "4NK Vault API",
"version": "1.0.0",
"domain": "vault.4nkweb.com",
"port": 6666,
"protocol": "HTTPS",
"encryption": "quantum-resistant",
"endpoints": {
"GET /<env>/<file>": "Sert un fichier chiffré depuis storage/<env>/<file>",
"GET /health": "Contrôle de santé",
"GET /info": "Informations sur l'API"
}
}
```
---
### 3. Service de fichiers
#### `GET /<env>/<file>`
Sert un fichier chiffré depuis le stockage.
**Paramètres d'URL** :
| Paramètre | Type | Requis | Description |
|-----------|------|--------|-------------|
| `env` | string | Oui | Environnement (ex: "dev", "prod") |
| `file` | string | Oui | Chemin du fichier relatif à `storage/<env>/` |
**Contraintes** :
- `env` : 1-50 caractères, alphanumériques et tirets uniquement
- `file` : 1-255 caractères, pas de `../` ou chemins absolus
**Headers de réponse** :
```
Content-Type: application/octet-stream
Content-Disposition: attachment; filename="<nom_du_fichier>"
X-Encryption-Type: quantum-resistant
X-Algorithm: X25519-ChaCha20-Poly1305
Content-Length: <taille_en_bytes>
```
**Codes de statut** :
| Code | Description | Cause possible |
|------|-------------|----------------|
| `200 OK` | Fichier servi avec succès | - |
| `403 Forbidden` | Accès non autorisé | Tentative d'accès hors du répertoire storage |
| `404 Not Found` | Fichier non trouvé | Fichier ou environnement inexistant |
| `500 Internal Server Error` | Erreur interne | Erreur de traitement ou de chiffrement |
**Exemples de requêtes** :
```bash
# Fichier de configuration Bitcoin
curl -k https://vault.4nkweb.com:6666/dev/bitcoin/bitcoin.conf
# Configuration Tor
curl -k https://vault.4nkweb.com:6666/dev/tor/torrc
# Configuration SDK Relay
curl -k https://vault.4nkweb.com:6666/dev/sdk_relay/sdk_relay.conf
```
**Exemple de réponse réussie** :
```
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Disposition: attachment; filename="bitcoin.conf"
X-Encryption-Type: quantum-resistant
X-Algorithm: X25519-ChaCha20-Poly1305
Content-Length: 1360
<contenu chiffré en base64>
```
**Exemples d'erreurs** :
```bash
# Fichier inexistant
curl -k https://vault.4nkweb.com:6666/dev/fichier-inexistant.conf
# Réponse: 404 Not Found
{
"error": "Fichier non trouvé: dev/fichier-inexistant.conf"
}
# Environnement inexistant
curl -k https://vault.4nkweb.com:6666/prod/bitcoin/bitcoin.conf
# Réponse: 404 Not Found
{
"error": "Fichier non trouvé: prod/bitcoin/bitcoin.conf"
}
# Tentative d'accès non autorisé
curl -k https://vault.4nkweb.com:6666/dev/../../../etc/passwd
# Réponse: 403 Forbidden
{
"error": "Accès non autorisé"
}
```
## Gestion des erreurs
### Format d'erreur standard
Toutes les erreurs suivent le même format JSON :
```json
{
"error": "Description de l'erreur",
"code": "ERROR_CODE",
"details": "Détails supplémentaires (optionnel)"
}
```
### Codes d'erreur HTTP
| Code | Erreur | Description | Solution |
|------|--------|-------------|----------|
| `400` | Bad Request | Requête malformée | Vérifier la syntaxe de la requête |
| `403` | Forbidden | Accès non autorisé | Vérifier les permissions et le chemin |
| `404` | Not Found | Ressource non trouvée | Vérifier l'existence du fichier |
| `408` | Request Timeout | Timeout de la requête | Réessayer ou augmenter le timeout |
| `500` | Internal Server Error | Erreur interne du serveur | Contacter le support |
| `503` | Service Unavailable | Service indisponible | Vérifier le statut du service |
### Types d'erreurs spécifiques
#### Erreurs de fichier
```json
{
"error": "Fichier non trouvé: <env>/<file>"
}
```
#### Erreurs d'accès
```json
{
"error": "Accès non autorisé"
}
```
#### Erreurs de traitement
```json
{
"error": "Erreur interne du serveur"
}
```
## Variables d'environnement
### Traitement automatique
L'API traite automatiquement les variables d'environnement dans les fichiers selon le format :
```
${VARIABLE_NAME}
```
### Résolution des variables
1. **Source** : `/home/debian/4NK_vault/storage/dev/.env`
2. **Format** : `KEY=VALUE`
3. **Résolution** : Récursive avec détection des dépendances circulaires
4. **Fallback** : Variables non résolues restent en format `${VAR_NAME}`
### Exemples de variables
#### Variables simples
```bash
# Dans .env
DOMAIN=4nkweb.com
PORT=6666
# Dans un fichier de configuration
server_name=${DOMAIN}
listen_port=${PORT}
```
#### Variables composites
```bash
# Dans .env
DOMAIN=4nkweb.com
HOST=dev4.$DOMAIN
ROOT_URL=https://$HOST
API_URL=$ROOT_URL/api
# Dans un fichier de configuration
api_endpoint=${API_URL}
```
### Gestion des erreurs de variables
- **Variable manquante** : Reste en format `${VAR_NAME}`
- **Dépendance circulaire** : Détectée et signalée dans les logs
- **Variable invalide** : Ignorée et conservée telle quelle
## Limites et quotas
### Limites par requête
| Paramètre | Limite | Description |
|-----------|--------|-------------|
| Taille de fichier | 10 MB | Maximum par fichier |
| Taille de requête | 1 MB | Maximum pour les paramètres |
| Timeout | 30s | Maximum par requête |
| Profondeur de variables | 10 | Maximum de niveaux de résolution |
### Limites par client
| Paramètre | Limite | Description |
|-----------|--------|-------------|
| Requêtes/minute | 100 | Par adresse IP |
| Requêtes/seconde | 10 | Burst autorisé |
| Connexions simultanées | 50 | Par adresse IP |
### Gestion des limites
```http
# Headers de limitation
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1640995200
X-RateLimit-Retry-After: 60
```
## Chiffrement
### Algorithme
- **Nom** : ChaCha20-Poly1305
- **Type** : Chiffrement authentifié
- **Résistance** : Quantique résistant
- **Performance** : Haute performance
### Paramètres
| Paramètre | Valeur | Description |
|-----------|--------|-------------|
| Clé | 32 bytes | 256 bits |
| Nonce | 12 bytes | 96 bits |
| Tag | 16 bytes | 128 bits (intégré) |
### Format des données chiffrées
```
Base64(nonce + ciphertext + tag)
```
Où :
- `nonce` : 12 bytes aléatoires
- `ciphertext` : Données chiffrées avec ChaCha20
- `tag` : Tag d'authentification Poly1305
## Exemples d'utilisation
### Récupération de fichier avec curl
```bash
#!/bin/bash
# Exemple complet de récupération de fichier
API_BASE="https://vault.4nkweb.com:6666"
ENV="dev"
FILE="bitcoin/bitcoin.conf"
# Vérification de la santé
echo "Vérification de la santé de l'API..."
curl -k -s "$API_BASE/health" | jq .
# Récupération du fichier
echo "Récupération du fichier $ENV/$FILE..."
curl -k -s "$API_BASE/$ENV/$FILE" -o "$FILE.encrypted"
# Vérification du fichier
if [ -f "$FILE.encrypted" ]; then
echo "Fichier récupéré: $(wc -c < "$FILE.encrypted") bytes"
echo "Début du contenu:"
head -c 200 "$FILE.encrypted"
echo ""
else
echo "Erreur: Fichier non récupéré"
fi
```
### Test de performance
```bash
#!/bin/bash
# Test de performance de l'API
API_BASE="https://vault.4nkweb.com:6666"
echo "Test de performance - 100 requêtes..."
# Test avec Apache Bench
ab -n 100 -c 10 -k "$API_BASE/health"
echo ""
echo "Test de récupération de fichier..."
# Test de fichier
time curl -k -s "$API_BASE/dev/bitcoin/bitcoin.conf" -o /dev/null
```
### Script de monitoring
```bash
#!/bin/bash
# Script de monitoring de l'API
API_BASE="https://vault.4nkweb.com:6666"
LOG_FILE="/var/log/vault-monitor.log"
# Fonction de log
log() {
echo "$(date): $1" >> "$LOG_FILE"
}
# Test de santé
response=$(curl -k -s -o /dev/null -w "%{http_code}" "$API_BASE/health")
if [ "$response" = "200" ]; then
log "API OK - Health check passed"
else
log "API ERROR - Health check failed (HTTP $response)"
# Envoyer une alerte
echo "ALERTE: API Vault inaccessible" | mail -s "Vault API Down" admin@4nkweb.com
fi
# Test de fichier
response=$(curl -k -s -o /dev/null -w "%{http_code}" "$API_BASE/dev/bitcoin/bitcoin.conf")
if [ "$response" = "200" ]; then
log "API OK - File access working"
else
log "API ERROR - File access failed (HTTP $response)"
fi
```
## Codes de statut détaillés
### 200 OK
**Cas d'usage** :
- Santé de l'API vérifiée
- Informations récupérées
- Fichier servi avec succès
**Headers typiques** :
```
HTTP/1.1 200 OK
Content-Type: application/json (pour /health, /info)
Content-Type: application/octet-stream (pour les fichiers)
X-Encryption-Type: quantum-resistant
X-Algorithm: X25519-ChaCha20-Poly1305
```
### 400 Bad Request
**Cas d'usage** :
- Paramètres manquants ou invalides
- Format de requête incorrect
**Exemple** :
```json
{
"error": "Paramètre 'env' manquant",
"code": "MISSING_PARAMETER"
}
```
### 403 Forbidden
**Cas d'usage** :
- Tentative d'accès hors du répertoire storage
- Chemins avec `../` ou absolus
**Exemple** :
```json
{
"error": "Accès non autorisé",
"code": "ACCESS_DENIED"
}
```
### 404 Not Found
**Cas d'usage** :
- Fichier inexistant
- Environnement inexistant
- Endpoint inexistant
**Exemple** :
```json
{
"error": "Fichier non trouvé: dev/fichier-inexistant.conf",
"code": "FILE_NOT_FOUND"
}
```
### 408 Request Timeout
**Cas d'usage** :
- Timeout de connexion
- Timeout de traitement
**Exemple** :
```json
{
"error": "Timeout de la requête",
"code": "REQUEST_TIMEOUT"
}
```
### 500 Internal Server Error
**Cas d'usage** :
- Erreur de déchiffrement
- Erreur de traitement des variables
- Erreur système
**Exemple** :
```json
{
"error": "Erreur interne du serveur",
"code": "INTERNAL_ERROR"
}
```
### 503 Service Unavailable
**Cas d'usage** :
- Service en maintenance
- Surcharge du serveur
**Exemple** :
```json
{
"error": "Service temporairement indisponible",
"code": "SERVICE_UNAVAILABLE"
}
```
## Versioning
### Version actuelle
- **Version API** : 1.0.0
- **Version SDK** : 1.0.0
- **Compatibilité** : Rétrocompatible
### Headers de version
```http
API-Version: 1.0.0
Supported-Versions: 1.0.0
```
### Politique de versioning
- **Versions majeures** : Breaking changes possibles
- **Versions mineures** : Nouvelles fonctionnalités, rétrocompatibles
- **Versions patch** : Corrections de bugs, rétrocompatibles
## Support et contact
### Documentation
- **Guide principal** : [docs/README.md](README.md)
- **Spécification API** : [docs/api-specification.md](api-specification.md)
- **Guide de déploiement** : [docs/deployment-guide.md](deployment-guide.md)
### Support technique
- **Issues** : [Git Issues](https://git.4nkweb.com/4nk/vault/issues)
- **Email** : api-support@4nkweb.com
- **Documentation** : [Wiki](https://git.4nkweb.com/4nk/vault/wiki)
### Statut du service
- **Page de statut** : https://status.4nkweb.com
- **Incidents** : Notifications via email et RSS
---
**Dernière mise à jour** : 2025-09-29
**Version API** : 1.0.0
**Prochaine révision** : 2025-12-29

303
docs/api-specification.md Normal file
View File

@ -0,0 +1,303 @@
# API Vault 4NK - Spécification technique
## Vue d'ensemble
L'API Vault 4NK est un service REST HTTPS qui fournit un accès sécurisé aux fichiers de configuration avec chiffrement quantique résistant.
## Informations de base
- **Base URL** : `https://vault.4nkweb.com:6666`
- **Protocole** : HTTPS uniquement
- **Format** : JSON pour les métadonnées, octet-stream pour les fichiers
- **Chiffrement** : ChaCha20-Poly1305 (quantique résistant)
## Endpoints
### 1. Contrôle de santé
#### `GET /health`
**Description** : Vérifie l'état de santé de l'API
**Réponse** :
```json
{
"status": "healthy",
"service": "vault-api",
"encryption": "quantum-resistant",
"algorithm": "X25519-ChaCha20-Poly1305"
}
```
**Codes de statut** :
- `200 OK` - API fonctionnelle
- `500 Internal Server Error` - Problème interne
---
### 2. Informations sur l'API
#### `GET /info`
**Description** : Retourne les informations détaillées sur l'API
**Réponse** :
```json
{
"name": "4NK Vault API",
"version": "1.0.0",
"domain": "vault.4nkweb.com",
"port": 6666,
"protocol": "HTTPS",
"encryption": "quantum-resistant",
"endpoints": {
"GET /<env>/<file>": "Sert un fichier chiffré depuis storage/<env>/<file>",
"GET /health": "Contrôle de santé",
"GET /info": "Informations sur l'API"
}
}
```
**Codes de statut** :
- `200 OK` - Informations récupérées
- `500 Internal Server Error` - Problème interne
---
### 3. Service de fichiers
#### `GET /<env>/<file>`
**Description** : Sert un fichier chiffré depuis le stockage
**Paramètres** :
- `env` (string, requis) : Environnement (ex: "dev", "prod")
- `file` (string, requis) : Chemin du fichier relatif à `storage/<env>/`
**Exemples** :
- `GET /dev/bitcoin/bitcoin.conf`
- `GET /dev/tor/torrc`
- `GET /prod/config/app.conf`
**Réponse** :
- **Content-Type** : `application/octet-stream`
- **Headers** :
- `X-Encryption-Type: quantum-resistant`
- `X-Algorithm: X25519-ChaCha20-Poly1305`
- `Content-Disposition: attachment; filename="<file>"`
- **Body** : Contenu chiffré en base64
**Codes de statut** :
- `200 OK` - Fichier servi avec succès
- `403 Forbidden` - Accès non autorisé (tentative d'accès hors du répertoire)
- `404 Not Found` - Fichier ou environnement non trouvé
- `500 Internal Server Error` - Erreur de traitement ou de chiffrement
**Exemple de requête** :
```bash
curl -k -H "Accept: application/octet-stream" \
https://vault.4nkweb.com:6666/dev/bitcoin/bitcoin.conf
```
**Exemple de réponse** :
```
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Disposition: attachment; filename="bitcoin.conf"
X-Encryption-Type: quantum-resistant
X-Algorithm: X25519-ChaCha20-Poly1305
Content-Length: 1360
<contenu chiffré en base64>
```
## Gestion des erreurs
### Format d'erreur standard
```json
{
"error": "Description de l'erreur",
"code": "ERROR_CODE",
"details": "Détails supplémentaires"
}
```
### Codes d'erreur
| Code HTTP | Erreur | Description |
|-----------|--------|-------------|
| 400 | Bad Request | Requête malformée |
| 403 | Forbidden | Accès non autorisé |
| 404 | Not Found | Ressource non trouvée |
| 408 | Request Timeout | Timeout de la requête |
| 500 | Internal Server Error | Erreur interne du serveur |
| 503 | Service Unavailable | Service indisponible |
### Exemples d'erreurs
**Fichier non trouvé** :
```json
{
"error": "Fichier non trouvé: dev/fichier-inexistant.conf"
}
```
**Accès non autorisé** :
```json
{
"error": "Accès non autorisé"
}
```
## Sécurité
### Chiffrement
L'API utilise ChaCha20-Poly1305 pour le chiffrement des fichiers :
- **Algorithme** : ChaCha20-Poly1305
- **Clé** : 32 bytes (256 bits)
- **Nonce** : 12 bytes aléatoires par fichier
- **Authentification** : Intégrée via Poly1305
### HTTPS
- **TLS** : Version 1.2 minimum
- **Certificats** : Auto-signés en développement, valides en production
- **Ciphers** : Suite cryptographique moderne uniquement
### Validation des chemins
L'API valide tous les chemins pour empêcher :
- Les accès en dehors du répertoire `storage/`
- Les traversées de répertoires (`../`)
- Les chemins absolus
## Variables d'environnement
### Traitement des variables
L'API traite automatiquement les variables d'environnement dans les fichiers :
**Format** : `${VAR_NAME}`
**Exemple** :
```bash
# Dans le fichier bitcoin.conf
datadir=$ROOT_DIR_LOGS/bitcoin
rpcport=$BITCOIN_SIGNET_RPC_PORT
# Dans .env
ROOT_DIR_LOGS=/home/debian/4NK_env/logs
BITCOIN_SIGNET_RPC_PORT=38332
```
### Variables composites
Les variables peuvent référencer d'autres variables :
```bash
DOMAIN=4nkweb.com
HOST=dev4.$DOMAIN
ROOT_URL=https://$HOST
API_URL=$ROOT_URL/api
```
### Résolution récursive
L'API résout récursivement les variables avec :
- Détection des dépendances circulaires
- Gestion des variables manquantes
- Logging des erreurs de résolution
## Limites et contraintes
### Taille des fichiers
- **Maximum** : 10 MB par fichier
- **Recommandé** : < 1 MB pour de meilleures performances
### Taux de requêtes
- **Limite** : 100 requêtes/minute par IP
- **Burst** : 10 requêtes/seconde
### Timeouts
- **Connexion** : 30 secondes
- **Lecture** : 60 secondes
- **Traitement** : 30 secondes
## Monitoring et logs
### Métriques disponibles
- Nombre de requêtes par endpoint
- Temps de réponse moyen
- Taux d'erreur par type
- Utilisation du chiffrement
### Logs
- Tous les accès aux fichiers
- Erreurs de déchiffrement
- Tentatives d'accès non autorisé
- Performance des requêtes
## Exemples d'utilisation
### Récupération d'un fichier simple
```bash
# Récupération du fichier bitcoin.conf
curl -k https://vault.4nkweb.com:6666/dev/bitcoin/bitcoin.conf \
-o bitcoin.conf.encrypted
# Le fichier est chiffré et doit être déchiffré côté client
```
### Vérification de l'état
```bash
# Vérification de la santé de l'API
curl -k https://vault.4nkweb.com:6666/health
# Récupération des informations
curl -k https://vault.4nkweb.com:6666/info
```
### Script de test
```bash
#!/bin/bash
# Test complet de l'API
API_BASE="https://vault.4nkweb.com:6666"
echo "Test de santé..."
curl -k -s "$API_BASE/health" | jq .
echo "Test d'informations..."
curl -k -s "$API_BASE/info" | jq .
echo "Test de fichier..."
curl -k -s "$API_BASE/dev/bitcoin/bitcoin.conf" -o test.encrypted
echo "Fichier récupéré: $(wc -c < test.encrypted) bytes"
```
## Versioning
L'API utilise un versioning sémantique :
- **Version actuelle** : 1.0.0
- **Compatibilité** : Les versions majeures peuvent introduire des breaking changes
- **Dépréciations** : Annoncées dans les headers et la documentation
## Support
Pour toute question sur l'API :
- **Documentation** : [docs/README.md](README.md)
- **Issues** : [Git Issues](https://git.4nkweb.com/4nk/vault/issues)
- **Email** : api-support@4nkweb.com
---
**Dernière mise à jour** : 2025-09-29
**Version API** : 1.0.0

594
docs/deployment-guide.md Normal file
View File

@ -0,0 +1,594 @@
# Guide de déploiement - 4NK Vault
## Vue d'ensemble
Ce guide couvre le déploiement complet du système 4NK Vault, incluant l'API serveur et le SDK client, dans différents environnements (développement, test, production).
## Prérequis
### Système
- **OS** : Linux (Ubuntu 20.04+, Debian 11+, CentOS 8+)
- **Python** : 3.8+
- **Node.js** : 16+ (pour le SDK)
- **RAM** : 512 MB minimum, 2 GB recommandé
- **Stockage** : 1 GB minimum pour les fichiers de configuration
### Réseau
- **Ports** : 6666 (HTTPS API)
- **DNS** : Résolution de `vault.4nkweb.com`
- **Certificats** : SSL/TLS pour la production
## Architecture de déploiement
```
┌─────────────────────────────────────────────────────────────┐
│ Load Balancer / Proxy │
│ (nginx, HAProxy, etc.) │
└─────────────────────┬───────────────────────────────────────┘
┌─────────────────────▼───────────────────────────────────────┐
│ API Vault Server │
│ (Python Flask) │
│ Port 6666 │
└─────────────────────┬───────────────────────────────────────┘
┌─────────────────────▼───────────────────────────────────────┐
│ Storage Layer │
│ /storage/<env>/<file>
└─────────────────────────────────────────────────────────────┘
```
## Déploiement en développement
### Installation rapide
```bash
# 1. Cloner le projet
git clone https://git.4nkweb.com/4nk/vault.git
cd vault
# 2. Installation des dépendances Python
pip install -r requirements.txt
# 3. Démarrage de l'API
./start_api.sh
```
### Configuration de développement
```bash
# Variables d'environnement pour le développement
export VAULT_ENV=dev
export VAULT_SSL_VERIFY=false
export VAULT_DEBUG=true
export VAULT_LOG_LEVEL=DEBUG
# Démarrage avec logs détaillés
python3 api_server.py --debug
```
### Test du déploiement
```bash
# Test de santé
curl -k https://localhost:6666/health
# Test de fichier
curl -k https://localhost:6666/dev/bitcoin/bitcoin.conf
# Test du SDK
cd sdk-client
npm install
npm run build
node dist/examples/basic-usage.js
```
## Déploiement en production
### 1. Préparation du serveur
#### Configuration système
```bash
# Mise à jour du système
sudo apt update && sudo apt upgrade -y
# Installation des dépendances
sudo apt install -y python3 python3-pip python3-venv nginx certbot
# Création de l'utilisateur vault
sudo useradd -r -s /bin/false vault
sudo mkdir -p /opt/vault
sudo chown vault:vault /opt/vault
```
#### Configuration réseau
```bash
# Ouverture du port 6666
sudo ufw allow 6666/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
```
### 2. Installation de l'API
#### Déploiement de l'application
```bash
# Copie des fichiers
sudo cp -r vault/* /opt/vault/
cd /opt/vault
# Création de l'environnement virtuel
sudo -u vault python3 -m venv venv
sudo -u vault venv/bin/pip install -r requirements.txt
# Configuration des permissions
sudo chown -R vault:vault /opt/vault
sudo chmod +x /opt/vault/start_api.sh
```
#### Configuration des variables d'environnement
```bash
# Création du fichier de configuration
sudo -u vault tee /opt/vault/.env << EOF
# Configuration de production
VAULT_ENV=production
VAULT_SSL_VERIFY=true
VAULT_LOG_LEVEL=INFO
VAULT_MAX_FILE_SIZE=10485760
VAULT_RATE_LIMIT=100
EOF
```
### 3. Configuration SSL/TLS
#### Certificats Let's Encrypt
```bash
# Installation de Certbot
sudo apt install certbot python3-certbot-nginx
# Génération du certificat
sudo certbot --nginx -d vault.4nkweb.com
# Renouvellement automatique
sudo crontab -e
# Ajouter : 0 12 * * * /usr/bin/certbot renew --quiet
```
#### Configuration manuelle des certificats
```bash
# Création des répertoires
sudo mkdir -p /etc/ssl/vault
sudo chown vault:vault /etc/ssl/vault
# Copie des certificats
sudo cp vault.crt /etc/ssl/vault/
sudo cp vault.key /etc/ssl/vault/
sudo chmod 600 /etc/ssl/vault/vault.key
sudo chmod 644 /etc/ssl/vault/vault.crt
```
### 4. Configuration du reverse proxy (Nginx)
#### Configuration Nginx
```nginx
# /etc/nginx/sites-available/vault
server {
listen 80;
server_name vault.4nkweb.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name vault.4nkweb.com;
# Certificats SSL
ssl_certificate /etc/letsencrypt/live/vault.4nkweb.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/vault.4nkweb.com/privkey.pem;
# Configuration SSL sécurisée
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# Headers de sécurité
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
add_header X-XSS-Protection "1; mode=block" always;
# Limitation du taux de requêtes
limit_req_zone $binary_remote_addr zone=vault:10m rate=10r/s;
limit_req zone=vault burst=20 nodelay;
# Proxy vers l'API
location / {
proxy_pass https://127.0.0.1:6666;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Timeouts
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
# Buffering
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
}
# Logs
access_log /var/log/nginx/vault.access.log;
error_log /var/log/nginx/vault.error.log;
}
```
#### Activation de la configuration
```bash
# Activation du site
sudo ln -s /etc/nginx/sites-available/vault /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
```
### 5. Configuration du service systemd
#### Création du service
```ini
# /etc/systemd/system/vault-api.service
[Unit]
Description=4NK Vault API Server
After=network.target
[Service]
Type=simple
User=vault
Group=vault
WorkingDirectory=/opt/vault
Environment=PATH=/opt/vault/venv/bin
Environment=VAULT_ENV=production
Environment=VAULT_SSL_VERIFY=true
Environment=VAULT_LOG_LEVEL=INFO
ExecStart=/opt/vault/venv/bin/python3 api_server.py
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=vault-api
# Sécurité
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/vault/storage
ReadWritePaths=/tmp
[Install]
WantedBy=multi-user.target
```
#### Activation du service
```bash
# Rechargement de systemd
sudo systemctl daemon-reload
# Activation au démarrage
sudo systemctl enable vault-api
# Démarrage du service
sudo systemctl start vault-api
# Vérification du statut
sudo systemctl status vault-api
```
### 6. Configuration du monitoring
#### Logs centralisés
```bash
# Configuration de rsyslog pour les logs de l'API
sudo tee /etc/rsyslog.d/50-vault.conf << EOF
# Logs de l'API Vault
:programname, isequal, "vault-api" /var/log/vault/api.log
& stop
EOF
sudo systemctl restart rsyslog
```
#### Métriques Prometheus
```python
# Extension de l'API pour les métriques
from prometheus_client import Counter, Histogram, generate_latest
REQUEST_COUNT = Counter('vault_requests_total', 'Total requests', ['method', 'endpoint'])
REQUEST_DURATION = Histogram('vault_request_duration_seconds', 'Request duration')
@app.route('/metrics')
def metrics():
return generate_latest()
```
### 7. Tests de production
#### Tests de santé
```bash
# Test de connectivité
curl -I https://vault.4nkweb.com/health
# Test de performance
ab -n 100 -c 10 https://vault.4nkweb.com/health
# Test SSL
openssl s_client -connect vault.4nkweb.com:443 -servername vault.4nkweb.com
```
#### Tests fonctionnels
```bash
# Test d'accès aux fichiers
curl -k https://vault.4nkweb.com/dev/bitcoin/bitcoin.conf
# Test du SDK
cd sdk-client
npm install
npm run build
node dist/examples/basic-usage.js
```
## Déploiement du SDK
### Publication NPM
```bash
# Configuration du registry privé
npm config set @4nk:registry https://npm.4nkweb.com/
# Publication
cd sdk-client
npm version patch
npm publish
# Installation
npm install @4nk/vault-sdk
```
### Déploiement dans les applications
#### Configuration
```typescript
// config/vault.ts
import { createVaultClient } from '@4nk/vault-sdk';
export const vaultClient = createVaultClient(
process.env.VAULT_API_URL || 'https://vault.4nkweb.com',
process.env.VAULT_DECRYPTION_KEY!
);
```
#### Utilisation
```typescript
// services/config.ts
import { vaultClient } from '../config/vault';
export async function loadConfig(env: string, configFile: string) {
try {
const file = await vaultClient.getFile(env, configFile);
return JSON.parse(file.content);
} catch (error) {
console.error('Erreur chargement config:', error);
throw error;
}
}
```
## Monitoring et maintenance
### Surveillance
#### Métriques système
```bash
# Surveillance des ressources
htop
iotop
netstat -tlnp | grep 6666
# Surveillance des logs
tail -f /var/log/vault/api.log
journalctl -u vault-api -f
```
#### Alertes
```bash
# Script d'alerte simple
#!/bin/bash
# /opt/vault/health-check.sh
API_URL="https://vault.4nkweb.com/health"
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" $API_URL)
if [ $RESPONSE -ne 200 ]; then
echo "ALERTE: API Vault inaccessible (HTTP $RESPONSE)" | mail -s "Vault API Down" admin@4nkweb.com
fi
```
### Maintenance
#### Mise à jour de l'application
```bash
# Sauvegarde
sudo systemctl stop vault-api
sudo cp -r /opt/vault /opt/vault.backup.$(date +%Y%m%d)
# Mise à jour
cd /opt/vault
sudo -u vault git pull
sudo -u vault venv/bin/pip install -r requirements.txt
# Redémarrage
sudo systemctl start vault-api
sudo systemctl status vault-api
```
#### Rotation des logs
```bash
# Configuration logrotate
sudo tee /etc/logrotate.d/vault << EOF
/var/log/vault/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
create 644 vault vault
postrotate
systemctl reload vault-api
endscript
}
EOF
```
## Sécurité en production
### Hardening du serveur
```bash
# Configuration du firewall
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
# Désactivation des services inutiles
sudo systemctl disable snapd
sudo systemctl stop snapd
```
### Audit de sécurité
```bash
# Audit des permissions
find /opt/vault -type f -perm /o+w
find /opt/vault -type d -perm /o+w
# Audit des ports ouverts
sudo netstat -tlnp
sudo ss -tlnp
# Audit des processus
ps aux | grep vault
```
## Dépannage
### Problèmes courants
#### Service ne démarre pas
```bash
# Vérification des logs
sudo journalctl -u vault-api -n 50
# Vérification des permissions
ls -la /opt/vault/
sudo chown -R vault:vault /opt/vault/
# Test manuel
sudo -u vault /opt/vault/venv/bin/python3 /opt/vault/api_server.py
```
#### Erreurs SSL
```bash
# Vérification des certificats
openssl x509 -in /etc/ssl/vault/vault.crt -text -noout
# Test de connectivité SSL
openssl s_client -connect vault.4nkweb.com:6666
# Renouvellement des certificats
sudo certbot renew
```
#### Problèmes de performance
```bash
# Surveillance des ressources
htop
iotop -a
# Analyse des logs de performance
grep "slow" /var/log/vault/api.log
# Optimisation de la configuration
# Augmentation des timeouts dans nginx
# Optimisation des paramètres Python
```
## Backup et récupération
### Stratégie de backup
```bash
#!/bin/bash
# /opt/vault/backup.sh
BACKUP_DIR="/opt/backups/vault"
DATE=$(date +%Y%m%d_%H%M%S)
# Création du répertoire de backup
mkdir -p $BACKUP_DIR
# Backup des fichiers de configuration
tar -czf $BACKUP_DIR/vault_config_$DATE.tar.gz /opt/vault/storage/
# Backup de la configuration système
cp /etc/systemd/system/vault-api.service $BACKUP_DIR/
cp /etc/nginx/sites-available/vault $BACKUP_DIR/
# Nettoyage des anciens backups (garde 30 jours)
find $BACKUP_DIR -name "*.tar.gz" -mtime +30 -delete
```
### Plan de récupération
1. **Récupération des fichiers** : Restauration depuis les backups
2. **Récupération de la configuration** : Restauration des fichiers de config
3. **Redémarrage des services** : `systemctl restart vault-api nginx`
4. **Tests de validation** : Vérification de la fonctionnalité
---
**Dernière mise à jour** : 2025-09-29
**Version** : 1.0.0

142
docs/index.md Normal file
View File

@ -0,0 +1,142 @@
# Documentation 4NK Vault
Bienvenue dans la documentation complète du projet 4NK Vault, un système de stockage sécurisé avec chiffrement quantique résistant.
## 📚 Documentation disponible
### 🚀 [Guide principal](README.md)
Vue d'ensemble du projet, architecture et utilisation de base.
### 🔧 [Spécification API](api-specification.md)
Spécification technique complète de l'API REST avec tous les détails d'implémentation.
### 📖 [Référence API](api-reference.md)
Référence complète des endpoints, paramètres, réponses et codes d'erreur.
### 🛡️ [Modèle de sécurité](security-model.md)
Architecture de sécurité, chiffrement quantique résistant et bonnes pratiques.
### 🚀 [Guide de déploiement](deployment-guide.md)
Instructions complètes pour déployer le système en développement et production.
### 💻 [Documentation SDK](sdk-documentation.md)
Documentation complète du SDK TypeScript avec exemples et API reference.
## 🎯 Démarrage rapide
### Installation et test
```bash
# 1. Installation des dépendances
pip install -r requirements.txt
# 2. Démarrage de l'API
./start_api.sh
# 3. Test de l'API
curl -k https://localhost:6666/health
# 4. Test du SDK
cd sdk-client
npm install && npm run build
node dist/examples/basic-usage.js
```
### Utilisation basique
```typescript
import { createVaultClient } from '@4nk/vault-sdk';
const client = createVaultClient(
'https://vault.4nkweb.com:6666',
'quantum_resistant_demo_key_32byt'
);
const file = await client.getFile('dev', 'bitcoin/bitcoin.conf');
console.log(file.content); // Contenu déchiffré
```
## 🔍 Navigation par cas d'usage
### Je veux...
- **Comprendre le projet** → [Guide principal](README.md)
- **Utiliser l'API** → [Référence API](api-reference.md)
- **Déployer le système** → [Guide de déploiement](deployment-guide.md)
- **Développer avec le SDK** → [Documentation SDK](sdk-documentation.md)
- **Comprendre la sécurité** → [Modèle de sécurité](security-model.md)
- **Implémenter l'API** → [Spécification API](api-specification.md)
## 🏗️ Architecture du système
```
┌─────────────────┐ HTTPS ┌─────────────────┐ ┌─────────────────┐
│ Client SDK │ ──────────► │ API Vault │ ──► │ Storage │
│ TypeScript │ Port 6666 │ Python Flask │ │ Files │
└─────────────────┘ └─────────────────┘ └─────────────────┘
┌─────────────────┐
│ Variables │
│ .env │
└─────────────────┘
```
## 🔐 Sécurité
- **Chiffrement** : ChaCha20-Poly1305 (quantique résistant)
- **Transport** : HTTPS uniquement (TLS 1.2+)
- **Authentification** : Chiffrement intégré avec nonce aléatoire
- **Validation** : Protection contre les accès non autorisés
## 📊 Fonctionnalités
- ✅ **API REST** : Endpoints pour la santé, info et fichiers
- ✅ **Chiffrement** : Quantique résistant avec ChaCha20-Poly1305
- ✅ **Variables** : Traitement automatique des variables composites
- ✅ **SDK TypeScript** : Client type-safe avec déchiffrement
- ✅ **Monitoring** : Logs détaillés et métriques
- ✅ **Sécurité** : Validation des chemins et protection d'accès
## 🛠️ Technologies
- **Backend** : Python 3.8+ avec Flask
- **Frontend** : TypeScript/JavaScript SDK
- **Chiffrement** : cryptography (Python), crypto (Node.js)
- **Transport** : HTTPS avec certificats SSL/TLS
- **Stockage** : Système de fichiers avec structure hiérarchique
## 📈 Versions
| Composant | Version | Statut |
|-----------|---------|--------|
| API Server | 1.0.0 | ✅ Stable |
| SDK Client | 1.0.0 | ✅ Stable |
| Documentation | 1.0.0 | ✅ Complète |
## 🤝 Support
### Ressources
- **Issues** : [Git Issues](https://git.4nkweb.com/4nk/vault/issues)
- **Wiki** : [Documentation Wiki](https://git.4nkweb.com/4nk/vault/wiki)
- **Email** : support@4nkweb.com
### Communauté
- **Discussions** : [Forum de la communauté](https://forum.4nkweb.com)
- **Chat** : [Discord](https://discord.gg/4nk-vault)
- **Newsletter** : [Abonnement](https://newsletter.4nkweb.com)
## 📄 Licence
MIT License - Voir le fichier `LICENSE` pour plus de détails.
## 🏷️ Tags et mots-clés
`vault` `security` `encryption` `quantum-resistant` `api` `typescript` `python` `chacha20` `poly1305` `https` `configuration` `variables` `sdk`
---
**Dernière mise à jour** : 2025-09-29
**Version documentation** : 1.0.0
**Maintenu par** : Équipe 4NK

635
docs/sdk-documentation.md Normal file
View File

@ -0,0 +1,635 @@
# SDK Client TypeScript - Documentation complète
## Vue d'ensemble
Le SDK Client TypeScript pour l'API Vault 4NK fournit une interface type-safe et moderne pour interagir avec l'API de stockage sécurisé. Il inclut le déchiffrement côté client, la gestion d'erreurs avancée, et des utilitaires cryptographiques.
## Installation
### NPM
```bash
npm install @4nk/vault-sdk
```
### Depuis les sources
```bash
git clone https://git.4nkweb.com/4nk/vault-sdk.git
cd vault-sdk
npm install
npm run build
```
## Configuration
### Importation de base
```typescript
import { VaultClient, createVaultClient, VaultCrypto } from '@4nk/vault-sdk';
```
### Création d'un client
#### Méthode simple
```typescript
const client = createVaultClient(
'https://vault.4nkweb.com:6666',
'quantum_resistant_demo_key_32byt'
);
```
#### Méthode avancée
```typescript
const client = new VaultClient(
{
baseUrl: 'https://vault.4nkweb.com:6666',
verifySsl: false, // Pour les certificats auto-signés
timeout: 15000, // 15 secondes
},
'quantum_resistant_demo_key_32byt'
);
```
## API Reference
### Classe VaultClient
#### Constructeur
```typescript
constructor(config: VaultConfig, decryptionKey: string)
```
**Paramètres** :
- `config` : Configuration du client
- `decryptionKey` : Clé de déchiffrement (32 bytes exactement)
**Configuration** :
```typescript
interface VaultConfig {
baseUrl: string; // URL de base de l'API
verifySsl?: boolean; // Validation SSL (défaut: true)
timeout?: number; // Timeout en ms (défaut: 30000)
}
```
#### Méthodes
##### `getFile(env: string, filePath: string): Promise<VaultFile>`
Récupère et déchiffre un fichier depuis l'API.
**Paramètres** :
- `env` : Environnement (ex: "dev", "prod")
- `filePath` : Chemin du fichier relatif
**Retour** :
```typescript
interface VaultFile {
content: string; // Contenu déchiffré
filename: string; // Nom du fichier
size: number; // Taille en caractères
encrypted: boolean; // Était chiffré
algorithm?: string; // Algorithme utilisé
}
```
**Exemple** :
```typescript
try {
const file = await client.getFile('dev', 'bitcoin/bitcoin.conf');
console.log(`Fichier: ${file.filename}`);
console.log(`Taille: ${file.size} caractères`);
console.log(`Contenu: ${file.content}`);
} catch (error) {
console.error('Erreur:', error.message);
}
```
##### `getFiles(requests: FileRequest[]): Promise<VaultFile[]>`
Récupère plusieurs fichiers en parallèle.
**Paramètres** :
```typescript
interface FileRequest {
env: string;
filePath: string;
}
```
**Exemple** :
```typescript
const files = await client.getFiles([
{ env: 'dev', filePath: 'bitcoin/bitcoin.conf' },
{ env: 'dev', filePath: 'tor/torrc' },
{ env: 'dev', filePath: 'sdk_relay/sdk_relay.conf' }
]);
files.forEach(file => {
console.log(`${file.filename}: ${file.size} caractères`);
});
```
##### `health(): Promise<VaultHealth>`
Vérifie l'état de santé de l'API.
**Retour** :
```typescript
interface VaultHealth {
status: string; // "healthy" ou "unhealthy"
service: string; // Nom du service
encryption: string; // Type de chiffrement
algorithm: string; // Algorithme utilisé
}
```
**Exemple** :
```typescript
const health = await client.health();
console.log(`Statut: ${health.status}`);
console.log(`Chiffrement: ${health.encryption}`);
```
##### `info(): Promise<VaultInfo>`
Récupère les informations sur l'API.
**Retour** :
```typescript
interface VaultInfo {
name: string; // Nom de l'API
version: string; // Version
domain: string; // Domaine
port: number; // Port
protocol: string; // Protocole
encryption: string; // Type de chiffrement
endpoints: Record<string, string>; // Endpoints disponibles
}
```
**Exemple** :
```typescript
const info = await client.info();
console.log(`API: ${info.name} v${info.version}`);
console.log(`Domaine: ${info.domain}:${info.port}`);
```
##### `ping(): Promise<boolean>`
Teste la connectivité à l'API.
**Retour** : `true` si connecté, `false` sinon
**Exemple** :
```typescript
const isConnected = await client.ping();
if (isConnected) {
console.log('✅ API accessible');
} else {
console.log('❌ API inaccessible');
}
```
##### `searchFiles(env: string, pattern?: RegExp): Promise<string[]>`
Recherche des fichiers par pattern (non implémenté côté serveur).
**Note** : Cette méthode retourne toujours un tableau vide car l'endpoint de recherche n'est pas encore implémenté côté serveur.
### Classe VaultCrypto
Utilitaires pour la gestion des clés de chiffrement.
#### `generateKey(): string`
Génère une clé de déchiffrement aléatoire de 32 bytes.
**Retour** : Clé de 32 caractères UTF-8
**Exemple** :
```typescript
const randomKey = VaultCrypto.generateKey();
console.log(`Clé générée: ${randomKey.substring(0, 10)}...`);
```
#### `hashToKey(password: string): string`
Dérive une clé de 32 bytes depuis un mot de passe.
**Paramètres** :
- `password` : Mot de passe source
**Retour** : Clé de 32 bytes dérivée avec SHA-256
**Exemple** :
```typescript
const password = 'mon-mot-de-passe-secret';
const derivedKey = VaultCrypto.hashToKey(password);
console.log(`Clé dérivée: ${derivedKey.substring(0, 10)}...`);
```
#### `validateKey(key: string): boolean`
Vérifie qu'une clé fait exactement 32 bytes.
**Paramètres** :
- `key` : Clé à valider
**Retour** : `true` si la clé est valide, `false` sinon
**Exemple** :
```typescript
const key = 'quantum_resistant_demo_key_32byt';
const isValid = VaultCrypto.validateKey(key);
console.log(`Clé valide: ${isValid}`);
```
## Gestion d'erreurs
### Classes d'erreurs
#### VaultApiError
Erreurs liées aux requêtes HTTP vers l'API.
```typescript
class VaultApiError extends Error {
constructor(
message: string,
public statusCode?: number,
public endpoint?: string
);
}
```
**Propriétés** :
- `message` : Message d'erreur
- `statusCode` : Code HTTP d'erreur
- `endpoint` : Endpoint qui a échoué
**Exemple** :
```typescript
try {
await client.getFile('dev', 'fichier-inexistant.conf');
} catch (error) {
if (error instanceof VaultApiError) {
console.error(`Erreur API: ${error.message}`);
console.error(`Code: ${error.statusCode}`);
console.error(`Endpoint: ${error.endpoint}`);
}
}
```
#### VaultDecryptionError
Erreurs liées au déchiffrement des fichiers.
```typescript
class VaultDecryptionError extends Error {
constructor(message: string);
}
```
**Exemple** :
```typescript
try {
const file = await client.getFile('dev', 'config.conf');
} catch (error) {
if (error instanceof VaultDecryptionError) {
console.error(`Erreur de déchiffrement: ${error.message}`);
}
}
```
### Gestion d'erreurs complète
```typescript
async function handleFileRequest(env: string, filePath: string) {
try {
const file = await client.getFile(env, filePath);
return file;
} catch (error) {
if (error instanceof VaultApiError) {
switch (error.statusCode) {
case 404:
console.error(`Fichier non trouvé: ${filePath}`);
break;
case 403:
console.error(`Accès non autorisé: ${filePath}`);
break;
case 500:
console.error(`Erreur serveur pour: ${filePath}`);
break;
default:
console.error(`Erreur API: ${error.message}`);
}
} else if (error instanceof VaultDecryptionError) {
console.error(`Erreur de déchiffrement: ${error.message}`);
} else {
console.error(`Erreur inconnue: ${error.message}`);
}
throw error;
}
}
```
## Exemples d'utilisation
### Exemple basique
```typescript
import { createVaultClient } from '@4nk/vault-sdk';
async function basicExample() {
// Création du client
const client = createVaultClient(
'https://vault.4nkweb.com:6666',
'quantum_resistant_demo_key_32byt'
);
// Test de connectivité
const isConnected = await client.ping();
if (!isConnected) {
throw new Error('Impossible de se connecter à l\'API');
}
// Récupération d'un fichier
const file = await client.getFile('dev', 'bitcoin/bitcoin.conf');
console.log(`Fichier: ${file.filename}`);
console.log(`Taille: ${file.size} caractères`);
console.log(`Contenu: ${file.content.substring(0, 100)}...`);
}
```
### Exemple avancé avec gestion d'erreurs
```typescript
import { VaultClient, VaultApiError, VaultDecryptionError } from '@4nk/vault-sdk';
async function advancedExample() {
const client = new VaultClient(
{
baseUrl: 'https://vault.4nkweb.com:6666',
timeout: 10000,
},
'quantum_resistant_demo_key_32byt'
);
// Informations sur l'API
try {
const info = await client.info();
console.log(`API: ${info.name} v${info.version}`);
} catch (error) {
console.error('Erreur info:', error.message);
}
// Récupération de plusieurs fichiers
const requests = [
{ env: 'dev', filePath: 'bitcoin/bitcoin.conf' },
{ env: 'dev', filePath: 'tor/torrc' },
{ env: 'dev', filePath: 'sdk_relay/sdk_relay.conf' }
];
try {
const files = await client.getFiles(requests);
console.log(`${files.length} fichiers récupérés`);
files.forEach((file, index) => {
console.log(`${index + 1}. ${file.filename}: ${file.size} chars`);
});
} catch (error) {
if (error instanceof VaultApiError) {
console.error(`Erreur API: ${error.statusCode} - ${error.message}`);
} else if (error instanceof VaultDecryptionError) {
console.error(`Erreur de déchiffrement: ${error.message}`);
}
}
}
```
### Exemple avec retry automatique
```typescript
async function retryOperation<T>(
operation: () => Promise<T>,
maxRetries: number = 3,
delay: number = 1000
): Promise<T> {
let lastError: Error;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
lastError = error as Error;
console.log(`Tentative ${attempt}/${maxRetries} échouée: ${lastError.message}`);
if (attempt < maxRetries) {
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
throw lastError!;
}
// Utilisation
const file = await retryOperation(
() => client.getFile('dev', 'bitcoin/bitcoin.conf'),
3,
500
);
```
## Configuration TypeScript
### tsconfig.json recommandé
```json
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020", "DOM"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"sourceMap": true
}
}
```
### Types personnalisés
```typescript
// Extension des types existants
interface CustomVaultFile extends VaultFile {
metadata?: {
lastModified: Date;
checksum: string;
};
}
// Types pour vos applications
interface ConfigFile {
environment: string;
services: string[];
variables: Record<string, string>;
}
```
## Tests
### Tests unitaires avec Jest
```typescript
import { VaultClient, VaultCrypto } from '@4nk/vault-sdk';
describe('VaultClient', () => {
let client: VaultClient;
beforeEach(() => {
client = new VaultClient(
{ baseUrl: 'https://vault.4nkweb.com:6666' },
'quantum_resistant_demo_key_32byt'
);
});
it('devrait récupérer un fichier', async () => {
const file = await client.getFile('dev', 'bitcoin/bitcoin.conf');
expect(file).toBeDefined();
expect(file.filename).toBe('bitcoin.conf');
expect(file.content).toBeTruthy();
});
it('devrait gérer les erreurs 404', async () => {
await expect(client.getFile('dev', 'fichier-inexistant.conf'))
.rejects
.toThrow('Fichier non trouvé');
});
});
```
### Tests d'intégration
```typescript
describe('Intégration API', () => {
it('devrait fonctionner end-to-end', async () => {
const client = createVaultClient(
'https://vault.4nkweb.com:6666',
'quantum_resistant_demo_key_32byt'
);
// Test de santé
const health = await client.health();
expect(health.status).toBe('healthy');
// Test de fichier
const file = await client.getFile('dev', 'bitcoin/bitcoin.conf');
expect(file.content).toContain('bitcoin');
});
});
```
## Performance et optimisation
### Récupération parallèle
```typescript
// ✅ Bon : Récupération parallèle
const files = await client.getFiles(requests);
// ❌ Mauvais : Récupération séquentielle
const files = [];
for (const request of requests) {
const file = await client.getFile(request.env, request.filePath);
files.push(file);
}
```
### Cache local
```typescript
class CachedVaultClient extends VaultClient {
private cache = new Map<string, VaultFile>();
async getFile(env: string, filePath: string): Promise<VaultFile> {
const key = `${env}/${filePath}`;
if (this.cache.has(key)) {
return this.cache.get(key)!;
}
const file = await super.getFile(env, filePath);
this.cache.set(key, file);
return file;
}
}
```
## Dépannage
### Problèmes courants
#### Erreur de clé de déchiffrement
```
Error: La clé de déchiffrement doit faire exactement 32 bytes
```
**Solution** : Vérifiez que votre clé fait exactement 32 caractères UTF-8.
#### Erreur de connectivité
```
Error: fetch failed
```
**Solutions** :
- Vérifiez que l'API est démarrée
- Vérifiez l'URL de base
- Désactivez la validation SSL si nécessaire (`verifySsl: false`)
#### Erreur de déchiffrement
```
VaultDecryptionError: Erreur de déchiffrement
```
**Solutions** :
- Vérifiez que la clé correspond à celle utilisée côté serveur
- Vérifiez que le fichier n'est pas corrompu
### Debug
```typescript
// Activation des logs détaillés
const client = new VaultClient(
{
baseUrl: 'https://vault.4nkweb.com:6666',
timeout: 30000
},
'quantum_resistant_demo_key_32byt'
);
// Test de connectivité
const isConnected = await client.ping();
console.log('Connecté:', isConnected);
// Test d'information
try {
const info = await client.info();
console.log('Info API:', info);
} catch (error) {
console.error('Erreur info:', error);
}
```
## Support
- **Documentation** : [docs/README.md](README.md)
- **API Specification** : [docs/api-specification.md](api-specification.md)
- **Issues** : [Git Issues](https://git.4nkweb.com/4nk/vault-sdk/issues)
- **Email** : sdk-support@4nkweb.com
---
**Version SDK** : 1.0.0
**Compatibilité API** : 1.0.0+

403
docs/security-model.md Normal file
View File

@ -0,0 +1,403 @@
# Modèle de sécurité - 4NK Vault
## Vue d'ensemble
Le modèle de sécurité du projet 4NK Vault repose sur plusieurs couches de protection pour assurer la confidentialité, l'intégrité et la disponibilité des données de configuration.
## Architecture de sécurité
```
┌─────────────────────────────────────────────────────────────┐
│ Couche de sécurité │
├─────────────────────────────────────────────────────────────┤
│ Application Layer │ HTTPS + Certificats SSL │
├─────────────────────────────────────────────────────────────┤
│ Transport Layer │ TLS 1.2+ avec ciphers modernes │
├─────────────────────────────────────────────────────────────┤
│ Encryption Layer │ ChaCha20-Poly1305 (quantique) │
├─────────────────────────────────────────────────────────────┤
│ Access Control │ Validation des chemins + .env │
├─────────────────────────────────────────────────────────────┤
│ Storage Layer │ Fichiers système sécurisés │
└─────────────────────────────────────────────────────────────┘
```
## Chiffrement quantique résistant
### Algorithme : ChaCha20-Poly1305
Le projet utilise ChaCha20-Poly1305, un algorithme de chiffrement authentifié qui est considéré comme résistant aux attaques quantiques à court terme.
#### Caractéristiques
- **ChaCha20** : Chiffrement de flux haute performance
- **Poly1305** : Authentification cryptographique
- **Résistance quantique** : Résistant aux attaques par ordinateur quantique
- **Performance** : Plus rapide qu'AES sur certains processeurs
#### Paramètres
```python
# Configuration du chiffrement
ALGORITHM = "ChaCha20-Poly1305"
KEY_SIZE = 32 # 256 bits
NONCE_SIZE = 12 # 96 bits
TAG_SIZE = 16 # 128 bits (intégré dans Poly1305)
```
### Gestion des clés
#### Génération des clés
```typescript
// Génération d'une clé aléatoire (32 bytes)
const key = VaultCrypto.generateKey();
// Dérivation depuis un mot de passe
const derivedKey = VaultCrypto.hashToKey('mon-mot-de-passe-secret');
// Validation de la clé
const isValid = VaultCrypto.validateKey(key);
```
#### Stockage des clés
- **Développement** : Clé de démonstration intégrée
- **Production** : Clés générées dynamiquement ou via HSM
- **Rotation** : Support de la rotation des clés
- **Validation** : Vérification stricte de la taille (32 bytes)
### Processus de chiffrement
#### Côté serveur (API)
```python
def encrypt_content(content: str, key: bytes) -> bytes:
# Génération d'un nonce aléatoire
nonce = os.urandom(12)
# Création du chiffreur
cipher = ChaCha20Poly1305(key)
# Chiffrement avec authentification
encrypted = cipher.encrypt(nonce, content.encode('utf-8'), None)
# Retour : nonce + données chiffrées
return base64.b64encode(nonce + encrypted)
```
#### Côté client (SDK)
```typescript
private decryptContent(encryptedContent: Buffer): string {
// Décodage base64
const decoded = Buffer.from(encryptedContent.toString(), 'base64');
// Extraction du nonce (12 premiers bytes)
const nonce = decoded.subarray(0, 12);
const ciphertext = decoded.subarray(12);
// Déchiffrement
const decipher = createDecipher('chacha20-poly1305', this.decryptionKey, nonce);
let decrypted = decipher.update(ciphertext, undefined, 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
```
## Sécurité du transport (HTTPS)
### Configuration TLS
```python
# Configuration SSL sécurisée
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.minimum_version = ssl.TLSVersion.TLSv1_2
context.set_ciphers('ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:!aNULL:!MD5:!DSS')
```
#### Suites cryptographiques supportées
- **ECDHE+AESGCM** : Échange de clés elliptique + AES-GCM
- **ECDHE+CHACHA20** : Échange de clés elliptique + ChaCha20
- **DHE+AESGCM** : Échange de clés Diffie-Hellman + AES-GCM
- **DHE+CHACHA20** : Échange de clés Diffie-Hellman + ChaCha20
#### Protocoles interdits
- **TLS 1.0/1.1** : Versions obsolètes
- **SSL** : Protocole obsolète
- **NULL** : Chiffrement nul
- **MD5** : Hash faible
- **DSS** : Algorithme faible
### Certificats
#### Développement
- **Type** : Certificats auto-signés
- **Génération** : Automatique au démarrage
- **Validation** : Désactivée côté client (`verifySsl: false`)
#### Production
- **Type** : Certificats signés par une CA
- **Validation** : Obligatoire côté client
- **Rotation** : Automatique avant expiration
## Contrôle d'accès
### Validation des chemins
```python
def validate_path(env: str, file_path: str) -> bool:
# Construction du chemin complet
full_path = STORAGE_ROOT / env / file_path
# Vérification de sécurité
if not str(full_path.resolve()).startswith(str(STORAGE_ROOT.resolve())):
return False
# Vérification de l'existence
return full_path.exists() and full_path.is_file()
```
#### Protection contre
- **Traversée de répertoires** : `../../../etc/passwd`
- **Chemins absolus** : `/etc/passwd`
- **Accès hors storage** : Toute tentative d'accès en dehors du répertoire `storage/`
### Variables d'environnement
#### Traitement sécurisé
```python
def resolve_variable(var_name: str, visited: set = None) -> str:
# Détection des dépendances circulaires
if visited is None:
visited = set()
if var_name in visited:
logger.warning(f"Dépendance circulaire détectée pour {var_name}")
return f"${{{var_name}}}"
# Résolution récursive sécurisée
visited.add(var_name)
value = self.variables[var_name]
# Traitement des sous-variables
pattern = r'\$\{([^}]+)\}'
matches = re.findall(pattern, value)
for match in matches:
resolved_value = self._resolve_variable(match, visited.copy())
value = value.replace(f"${{{match}}}", resolved_value)
return value
```
#### Sécurité des variables
- **Validation** : Vérification de la syntaxe des variables
- **Circularité** : Détection des dépendances circulaires
- **Injection** : Protection contre l'injection de code
- **Limitation** : Limite de profondeur de résolution
## Audit et monitoring
### Logging sécurisé
```python
# Logs d'accès
logger.info(f"Served file: {env}/{file_path}")
# Logs d'erreur
logger.error(f"Erreur lors du service du fichier {env}/{file_path}: {e}")
# Logs de sécurité
logger.warning(f"Tentative d'accès non autorisé: {file_path}")
```
#### Types de logs
- **Accès** : Tous les accès aux fichiers
- **Erreurs** : Erreurs de traitement et de chiffrement
- **Sécurité** : Tentatives d'accès non autorisé
- **Performance** : Métriques de performance
### Métriques de sécurité
```python
# Métriques collectées
security_metrics = {
'failed_decryptions': 0,
'unauthorized_access_attempts': 0,
'invalid_paths_attempted': 0,
'circular_dependency_detections': 0
}
```
## Gestion des secrets
### Clés de chiffrement
#### Développement
```python
# Clé de démonstration (à ne jamais utiliser en production)
DEMO_KEY = b'quantum_resistant_demo_key_32byt'
```
#### Production
```python
# Génération sécurisée des clés
def generate_production_key():
# Utilisation d'un générateur cryptographiquement sécurisé
return os.urandom(32)
# Stockage sécurisé (exemple avec HSM)
def load_key_from_hsm(key_id: str):
# Intégration avec un Hardware Security Module
return hsm_client.get_key(key_id)
```
### Rotation des clés
```python
class KeyRotationManager:
def __init__(self):
self.current_key = self.load_current_key()
self.next_key = self.generate_next_key()
def rotate_keys(self):
# Rotation des clés
old_key = self.current_key
self.current_key = self.next_key
self.next_key = self.generate_next_key()
# Nettoyage de l'ancienne clé
self.secure_delete(old_key)
```
## Bonnes pratiques de sécurité
### Développement
#### Clés de démonstration
```typescript
// ✅ Bon : Clé de démonstration clairement identifiée
const DEMO_KEY = 'quantum_resistant_demo_key_32byt';
// ❌ Mauvais : Clé de production en dur
const PROD_KEY = 'real-production-key-here';
```
#### Validation des entrées
```typescript
// ✅ Bon : Validation stricte
if (!VaultCrypto.validateKey(key)) {
throw new Error('Clé invalide');
}
// ❌ Mauvais : Pas de validation
const cipher = createDecipher('chacha20-poly1305', key);
```
### Production
#### Configuration sécurisée
```python
# ✅ Bon : Configuration de production
PRODUCTION_CONFIG = {
'verify_ssl': True,
'cert_validation': True,
'key_rotation_interval': 86400, # 24h
'max_file_size': 10 * 1024 * 1024, # 10MB
'rate_limit': 100 # req/min
}
```
#### Monitoring
```python
# ✅ Bon : Monitoring de sécurité
def log_security_event(event_type: str, details: dict):
security_logger.warning({
'event': event_type,
'timestamp': datetime.utcnow(),
'details': details,
'severity': 'HIGH'
})
```
## Conformité et standards
### Standards de chiffrement
- **NIST SP 800-57** : Recommandations pour la gestion des clés
- **FIPS 140-2** : Standards pour les modules cryptographiques
- **Common Criteria** : Évaluation de la sécurité des produits IT
### Algorithmes approuvés
- **ChaCha20** : RFC 8439
- **Poly1305** : RFC 8439
- **SHA-256** : FIPS 180-4
- **TLS 1.2+** : RFC 5246, RFC 8446
### Résistance quantique
Le choix de ChaCha20-Poly1305 offre une résistance quantique à court terme :
- **Attaques classiques** : Résistance prouvée
- **Attaques quantiques** : Résistance estimée jusqu'en 2030
- **Migration** : Plan de migration vers des algorithmes post-quantiques
## Incident Response
### Procédure d'incident
1. **Détection** : Monitoring automatique des anomalies
2. **Analyse** : Investigation des logs et métriques
3. **Containment** : Isolation des systèmes affectés
4. **Éradication** : Correction des vulnérabilités
5. **Récupération** : Remise en service sécurisée
6. **Lessons learned** : Amélioration des procédures
### Contacts de sécurité
- **Équipe sécurité** : security@4nkweb.com
- **Incident response** : incident@4nkweb.com
- **Urgence** : +33 1 XX XX XX XX
## Mise à jour et maintenance
### Gestion des vulnérabilités
```python
# Processus de mise à jour de sécurité
def update_security_patches():
# Vérification des mises à jour
patches = check_security_updates()
for patch in patches:
if patch.severity >= 'HIGH':
apply_patch(patch)
notify_security_team(patch)
```
### Tests de sécurité
```bash
# Tests de sécurité automatisés
npm run test:security
python3 -m pytest tests/security/
# Audit de sécurité
npm audit
pip-audit
```
---
**Dernière révision** : 2025-09-29
**Version** : 1.0.0
**Prochaine révision** : 2025-12-29

3
requirements.txt Normal file
View File

@ -0,0 +1,3 @@
Flask==2.3.3
cryptography==41.0.7
Werkzeug==2.3.7

341
sdk-client/README.md Normal file
View File

@ -0,0 +1,341 @@
# SDK Client TypeScript pour l'API Vault 4NK
Un SDK TypeScript moderne et type-safe pour interagir avec l'API Vault 4NK, permettant de récupérer et déchiffrer des fichiers depuis un service de stockage sécurisé avec chiffrement quantique résistant.
## 🚀 Caractéristiques
- **Type-safe** : Entièrement écrit en TypeScript avec types stricts
- **Chiffrement quantique résistant** : Support de ChaCha20-Poly1305
- **Gestion d'erreurs avancée** : Classes d'erreurs spécialisées
- **Requêtes parallèles** : Récupération de plusieurs fichiers simultanément
- **Configuration flexible** : Timeouts, SSL, etc.
- **Utilitaires crypto** : Génération et validation de clés
- **Monitoring** : Endpoints de santé et d'information
## 📦 Installation
```bash
npm install @4nk/vault-sdk
```
## 🔧 Configuration
### Clé de déchiffrement
Le SDK nécessite une clé de déchiffrement de 32 bytes pour déchiffrer les fichiers :
```typescript
import { VaultCrypto } from '@4nk/vault-sdk';
// Génération d'une clé aléatoire
const randomKey = VaultCrypto.generateKey();
// Ou dérivation depuis un mot de passe
const derivedKey = VaultCrypto.hashToKey('mon-mot-de-passe-secret');
// Validation d'une clé
const isValid = VaultCrypto.validateKey(myKey);
```
## 📖 Utilisation
### Exemple basique
```typescript
import { createVaultClient } from '@4nk/vault-sdk';
// Création du client
const client = createVaultClient(
'https://vault.4nkweb.com:6666',
'quantum_resistant_demo_key_32_bytes!'
);
// Récupération d'un fichier
try {
const file = await client.getFile('dev', 'bitcoin/bitcoin.conf');
console.log(`Contenu: ${file.content}`);
console.log(`Taille: ${file.size} caractères`);
console.log(`Chiffré: ${file.encrypted ? 'Oui' : 'Non'}`);
} catch (error) {
console.error('Erreur:', error.message);
}
```
### Configuration avancée
```typescript
import { VaultClient } from '@4nk/vault-sdk';
const client = new VaultClient(
{
baseUrl: 'https://vault.4nkweb.com:6666',
verifySsl: false, // Pour les certificats auto-signés
timeout: 10000, // 10 secondes
},
'quantum_resistant_demo_key_32_bytes!'
);
```
### Récupération de plusieurs fichiers
```typescript
// Récupération parallèle
const files = await client.getFiles([
{ env: 'dev', filePath: 'bitcoin/bitcoin.conf' },
{ env: 'dev', filePath: 'tor/torrc' },
{ env: 'dev', filePath: 'sdk_relay/sdk_relay.conf' }
]);
files.forEach(file => {
console.log(`${file.filename}: ${file.size} caractères`);
});
```
### Monitoring et santé
```typescript
// Test de connectivité
const isConnected = await client.ping();
console.log(`Connecté: ${isConnected}`);
// Informations sur l'API
const info = await client.info();
console.log(`API: ${info.name} v${info.version}`);
// État de santé
const health = await client.health();
console.log(`Statut: ${health.status}`);
```
## 🛡️ Gestion d'erreurs
Le SDK fournit des classes d'erreurs spécialisées :
```typescript
import { VaultApiError, VaultDecryptionError } from '@4nk/vault-sdk';
try {
await client.getFile('dev', 'fichier-inexistant.conf');
} catch (error) {
if (error instanceof VaultApiError) {
console.error(`Erreur API: ${error.message} (${error.statusCode})`);
console.error(`Endpoint: ${error.endpoint}`);
} else if (error instanceof VaultDecryptionError) {
console.error(`Erreur de déchiffrement: ${error.message}`);
} else {
console.error(`Erreur inconnue: ${error.message}`);
}
}
```
### Types d'erreurs
- **`VaultApiError`** : Erreurs HTTP de l'API
- `statusCode` : Code d'erreur HTTP
- `endpoint` : Endpoint qui a échoué
- **`VaultDecryptionError`** : Erreurs de déchiffrement
- Clé incorrecte
- Données corrompues
- Format invalide
## 🔐 Sécurité
### Chiffrement
- **Algorithme** : ChaCha20-Poly1305
- **Nonce** : 12 bytes aléatoires par fichier
- **Clé** : 32 bytes (256 bits)
- **Authentification** : Intégrée via Poly1305
### Bonnes pratiques
```typescript
// ✅ Utiliser des clés générées aléatoirement
const key = VaultCrypto.generateKey();
// ✅ Valider les clés avant utilisation
if (!VaultCrypto.validateKey(key)) {
throw new Error('Clé invalide');
}
// ✅ Gérer les erreurs de déchiffrement
try {
const file = await client.getFile('dev', 'config.conf');
} catch (error) {
if (error instanceof VaultDecryptionError) {
// Clé incorrecte ou données corrompues
console.error('Erreur de déchiffrement');
}
}
// ✅ Utiliser HTTPS en production
const client = new VaultClient(
{
baseUrl: 'https://vault.4nkweb.com:6666',
verifySsl: true // Activer la validation SSL
},
key
);
```
## 📚 API Reference
### Classes principales
#### `VaultClient`
```typescript
class VaultClient {
constructor(config: VaultConfig, decryptionKey: string)
// Récupération de fichiers
getFile(env: string, filePath: string): Promise<VaultFile>
getFiles(requests: FileRequest[]): Promise<VaultFile[]>
// Monitoring
health(): Promise<VaultHealth>
info(): Promise<VaultInfo>
ping(): Promise<boolean>
// Utilitaires
searchFiles(env: string, pattern?: RegExp): Promise<string[]>
}
```
#### `VaultCrypto`
```typescript
class VaultCrypto {
// Génération de clés
static generateKey(): string
static hashToKey(password: string): string
static validateKey(key: string): boolean
}
```
### Types
```typescript
interface VaultFile {
content: string;
filename: string;
size: number;
encrypted: boolean;
algorithm?: string;
}
interface VaultHealth {
status: string;
service: string;
encryption: string;
algorithm: string;
}
interface VaultConfig {
baseUrl: string;
verifySsl?: boolean;
timeout?: number;
}
```
## 🧪 Tests et exemples
### Exemples fournis
- **`basic-usage.ts`** : Utilisation simple du SDK
- **`advanced-usage.ts`** : Fonctionnalités avancées et performance
- **`error-handling.ts`** : Gestion d'erreurs complète
### Exécution des exemples
```bash
# Compilation
npm run build
# Exemple basique
node dist/examples/basic-usage.js
# Exemple avancé
node dist/examples/advanced-usage.js
# Gestion d'erreurs
node dist/examples/error-handling.js
```
### Tests unitaires
```bash
npm test
```
## 🔧 Développement
### Prérequis
- Node.js >= 16.0.0
- TypeScript >= 4.0.0
### Installation des dépendances
```bash
npm install
```
### Compilation
```bash
npm run build
```
### Linting
```bash
npm run lint
```
### Structure du projet
```
sdk-client/
├── src/
│ └── index.ts # Code principal du SDK
├── examples/
│ ├── basic-usage.ts # Exemple basique
│ ├── advanced-usage.ts # Exemple avancé
│ └── error-handling.ts # Gestion d'erreurs
├── dist/ # Code compilé
├── package.json
├── tsconfig.json
└── README.md
```
## 🌐 Compatibilité
- **Node.js** : >= 16.0.0
- **TypeScript** : >= 4.0.0
- **Navigateurs** : Support des APIs modernes (fetch, crypto)
## 📄 Licence
MIT License - Voir le fichier `LICENSE` pour plus de détails.
## 🤝 Contribution
1. Fork le projet
2. Créer une branche feature (`git checkout -b feature/nouvelle-fonctionnalite`)
3. Commit les changements (`git commit -am 'Ajouter nouvelle fonctionnalité'`)
4. Push vers la branche (`git push origin feature/nouvelle-fonctionnalite`)
5. Ouvrir une Pull Request
## 📞 Support
- **Issues** : [Git Issues](https://git.4nkweb.com/4nk/vault-sdk/issues)
- **Documentation** : [Wiki du projet](https://git.4nkweb.com/4nk/vault-sdk/wiki)
- **Email** : support@4nkweb.com
---
**Version** : 1.0.0
**API Vault** : vault.4nkweb.com:6666
**Chiffrement** : ChaCha20-Poly1305 (quantique résistant)

193
sdk-client/demo.ts Normal file
View File

@ -0,0 +1,193 @@
#!/usr/bin/env node
/**
* Démonstration du SDK Vault 4NK en action
* Utilise l'API réelle pour récupérer et déchiffrer des fichiers
*/
import { VaultClient, VaultCrypto } from './src/index';
async function demo() {
console.log('🚀 Démonstration du SDK Vault 4NK');
console.log('=' .repeat(50));
console.log('📡 Connexion à l\'API: https://vault.4nkweb.com:6666');
console.log('🔐 Chiffrement: ChaCha20-Poly1305 (quantique résistant)');
console.log('');
try {
// 1. Création du client avec la clé de déchiffrement
console.log('🔑 Initialisation du client...');
const client = new VaultClient(
{
baseUrl: 'https://vault.4nkweb.com:6666',
verifySsl: false, // Certificats auto-signés
timeout: 15000, // 15 secondes de timeout
},
'quantum_resistant_demo_key_32byt' // Clé de démonstration (32 bytes)
);
// 2. Test de connectivité
console.log('🌐 Test de connectivité...');
const isConnected = await client.ping();
if (!isConnected) {
throw new Error('❌ Impossible de se connecter à l\'API Vault');
}
console.log('✅ Connecté à l\'API Vault');
// 3. Informations sur l'API
console.log('\n📋 Informations sur l\'API...');
const info = await client.info();
console.log(` Nom: ${info.name}`);
console.log(` Version: ${info.version}`);
console.log(` Domaine: ${info.domain}:${info.port}`);
console.log(` Protocole: ${info.protocol}`);
console.log(` Chiffrement: ${info.encryption}`);
// 4. État de santé
console.log('\n🏥 État de santé...');
const health = await client.health();
console.log(` Statut: ${health.status}`);
console.log(` Service: ${health.service}`);
console.log(` Algorithme: ${health.algorithm}`);
// 5. Récupération et déchiffrement de fichiers
console.log('\n📁 Récupération de fichiers chiffrés...');
const filesToGet = [
{ env: 'dev', path: 'bitcoin/bitcoin.conf', description: 'Configuration Bitcoin' },
{ env: 'dev', path: 'tor/torrc', description: 'Configuration Tor' },
{ env: 'dev', path: 'sdk_relay/sdk_relay.conf', description: 'Configuration SDK Relay' }
];
for (const fileInfo of filesToGet) {
console.log(`\n 📄 ${fileInfo.description} (${fileInfo.path})`);
try {
const startTime = Date.now();
const file = await client.getFile(fileInfo.env, fileInfo.path);
const endTime = Date.now();
console.log(` ✅ Récupéré en ${endTime - startTime}ms`);
console.log(` 📦 Taille: ${file.size} caractères`);
console.log(` 🔐 Chiffré: ${file.encrypted ? 'Oui' : 'Non'}`);
console.log(` 🔧 Algorithme: ${file.algorithm || 'N/A'}`);
// Analyse du contenu
const lines = file.content.split('\n');
const configLines = lines.filter(line =>
line.trim() && !line.trim().startsWith('#')
);
console.log(` 📝 Lignes totales: ${lines.length}`);
console.log(` ⚙️ Lignes de config: ${configLines.length}`);
// Recherche de variables d'environnement
const envVars = file.content.match(/\$\{[^}]+\}/g) || [];
if (envVars.length > 0) {
const uniqueVars = [...new Set(envVars)];
console.log(` 🔧 Variables env: ${uniqueVars.length} (${uniqueVars.slice(0, 3).join(', ')}...)`);
}
// Affichage d'un échantillon du contenu déchiffré
const sample = file.content.substring(0, 150).replace(/\n/g, '\\n');
console.log(` 📖 Échantillon: ${sample}...`);
} catch (error) {
console.log(` ❌ Erreur: ${error instanceof Error ? error.message : error}`);
}
}
// 6. Test de récupération parallèle
console.log('\n⚡ Test de récupération parallèle...');
const parallelStartTime = Date.now();
const parallelRequests = [
{ env: 'dev', filePath: 'bitcoin/bitcoin.conf' },
{ env: 'dev', filePath: 'tor/torrc' },
{ env: 'dev', filePath: 'sdk_relay/sdk_relay.conf' },
{ env: 'dev', filePath: 'supervisor/supervisord.conf' }
];
try {
const files = await client.getFiles(parallelRequests);
const parallelEndTime = Date.now();
console.log(`${files.length} fichiers récupérés en ${parallelEndTime - parallelStartTime}ms`);
console.log(` 📊 Temps moyen: ${Math.round((parallelEndTime - parallelStartTime) / files.length)}ms par fichier`);
const totalSize = files.reduce((sum, file) => sum + file.size, 0);
console.log(` 📦 Taille totale: ${totalSize} caractères`);
// Statistiques
const encryptedCount = files.filter(f => f.encrypted).length;
console.log(` 🔐 Fichiers chiffrés: ${encryptedCount}/${files.length}`);
} catch (error) {
console.log(` ❌ Erreur récupération parallèle: ${error instanceof Error ? error.message : error}`);
}
// 7. Test des utilitaires crypto
console.log('\n🔐 Test des utilitaires de chiffrement...');
// Génération d'une clé aléatoire
const randomKey = VaultCrypto.generateKey();
console.log(` 🔑 Clé générée: ${randomKey.substring(0, 10)}... (${randomKey.length} chars)`);
console.log(` ✅ Clé valide: ${VaultCrypto.validateKey(randomKey) ? 'Oui' : 'Non'}`);
// Dérivation depuis un mot de passe
const password = 'mon-mot-de-passe-secret-demo';
const derivedKey = VaultCrypto.hashToKey(password);
console.log(` 🔐 Mot de passe: ${password}`);
console.log(` 🔑 Clé dérivée: ${derivedKey.substring(0, 10)}...`);
console.log(` ✅ Clé dérivée valide: ${VaultCrypto.validateKey(derivedKey) ? 'Oui' : 'Non'}`);
// 8. Test de gestion d'erreurs
console.log('\n🛡 Test de gestion d\'erreurs...');
// Fichier inexistant
try {
await client.getFile('dev', 'fichier-inexistant.conf');
console.log(' ❌ Erreur: Devrait échouer');
} catch (error) {
console.log(` ✅ Fichier inexistant: ${error instanceof Error ? error.message : error}`);
}
// Environnement inexistant
try {
await client.getFile('prod', 'bitcoin/bitcoin.conf');
console.log(' ❌ Erreur: Devrait échouer');
} catch (error) {
console.log(` ✅ Environnement inexistant: ${error instanceof Error ? error.message : error}`);
}
console.log('\n🎉 Démonstration terminée avec succès!');
console.log('');
console.log('📋 Résumé:');
console.log(' ✅ API Vault opérationnelle');
console.log(' ✅ Chiffrement quantique résistant fonctionnel');
console.log(' ✅ Déchiffrement côté client réussi');
console.log(' ✅ Variables d\'environnement traitées');
console.log(' ✅ Récupération parallèle efficace');
console.log(' ✅ Gestion d\'erreurs robuste');
} catch (error) {
console.error('\n❌ Erreur fatale:', error instanceof Error ? error.message : error);
console.error('');
console.error('🔍 Vérifications possibles:');
console.error(' • L\'API Vault est-elle démarrée ?');
console.error(' • Le port 6666 est-il accessible ?');
console.error(' • Les certificats SSL sont-ils valides ?');
console.error(' • La clé de déchiffrement est-elle correcte ?');
process.exit(1);
}
}
// Exécution de la démonstration
if (require.main === module) {
demo().catch(error => {
console.error('❌ Erreur inattendue:', error);
process.exit(1);
});
}
export { demo };

View File

@ -0,0 +1,184 @@
/**
* Exemple d'utilisation avancée du SDK Vault 4NK
* Démontre les fonctionnalités avancées et la gestion d'erreurs
*/
import { VaultClient, VaultCrypto, VaultApiError, VaultDecryptionError } from '../src/index';
async function advancedExample() {
console.log('🚀 Exemple d\'utilisation avancée du SDK Vault 4NK');
console.log('=' .repeat(60));
try {
// 1. Génération d'une clé de chiffrement personnalisée
console.log('🔑 Génération d\'une clé de chiffrement...');
const customKey = VaultCrypto.generateKey();
console.log(` Clé générée: ${customKey.substring(0, 10)}...`);
// Validation de la clé
const isValid = VaultCrypto.validateKey(customKey);
console.log(` Clé valide: ${isValid ? 'Oui' : 'Non'}`);
// 2. Création du client avec configuration personnalisée
console.log('\n⚙ Configuration du client...');
const client = new VaultClient(
{
baseUrl: 'https://vault.4nkweb.com:6666',
verifySsl: false, // Désactivé pour les certificats auto-signés
timeout: 10000, // Timeout de 10 secondes
},
'quantum_resistant_demo_key_32_bytes!' // Clé de démonstration
);
// 3. Gestion d'erreurs avancée
console.log('\n🛡 Test de gestion d\'erreurs...');
// Test avec un fichier inexistant
try {
await client.getFile('dev', 'fichier-inexistant.conf');
} catch (error) {
if (error instanceof VaultApiError) {
console.log(` ✅ Erreur API capturée: ${error.message} (${error.statusCode})`);
} else {
console.log(` ❌ Erreur inattendue: ${error}`);
}
}
// Test avec un environnement inexistant
try {
await client.getFile('prod', 'bitcoin/bitcoin.conf');
} catch (error) {
if (error instanceof VaultApiError) {
console.log(` ✅ Erreur environnement: ${error.message} (${error.statusCode})`);
}
}
// 4. Récupération de fichiers avec analyse de contenu
console.log('\n📊 Analyse de contenu des fichiers...');
const filePaths = [
'bitcoin/bitcoin.conf',
'tor/torrc',
'sdk_relay/sdk_relay.conf'
];
for (const filePath of filePaths) {
try {
const file = await client.getFile('dev', filePath);
console.log(`\n 📄 ${file.filename}:`);
console.log(` Taille: ${file.size} caractères`);
console.log(` Chiffré: ${file.encrypted ? 'Oui' : 'Non'}`);
// Analyse du contenu
const lines = file.content.split('\n');
const configLines = lines.filter(line =>
line.trim() && !line.trim().startsWith('#')
);
console.log(` Lignes totales: ${lines.length}`);
console.log(` Lignes de config: ${configLines.length}`);
// Recherche de variables d'environnement
const envVars = file.content.match(/\$\{[^}]+\}/g) || [];
if (envVars.length > 0) {
console.log(` Variables env: ${envVars.length} (${envVars.slice(0, 3).join(', ')}...)`);
}
} catch (error) {
console.log(` ❌ Erreur pour ${filePath}: ${error instanceof Error ? error.message : error}`);
}
}
// 5. Test de performance avec récupération parallèle
console.log('\n⚡ Test de performance...');
const startTime = Date.now();
const parallelRequests = [
{ env: 'dev', filePath: 'bitcoin/bitcoin.conf' },
{ env: 'dev', filePath: 'tor/torrc' },
{ env: 'dev', filePath: 'sdk_relay/sdk_relay.conf' },
{ env: 'dev', filePath: 'supervisor/supervisord.conf' }
];
const files = await client.getFiles(parallelRequests);
const endTime = Date.now();
console.log(` Fichiers récupérés: ${files.length}`);
console.log(` Temps total: ${endTime - startTime}ms`);
console.log(` Temps moyen par fichier: ${Math.round((endTime - startTime) / files.length)}ms`);
const totalSize = files.reduce((sum, file) => sum + file.size, 0);
console.log(` Taille totale: ${totalSize} caractères`);
// 6. Monitoring et métriques
console.log('\n📈 Monitoring et métriques...');
// Test de connectivité répété
const pingTests = 5;
const pingResults: boolean[] = [];
for (let i = 0; i < pingTests; i++) {
const isConnected = await client.ping();
pingResults.push(isConnected);
await new Promise(resolve => setTimeout(resolve, 100)); // Délai de 100ms
}
const successRate = (pingResults.filter(r => r).length / pingTests) * 100;
console.log(` Tests de connectivité: ${pingTests}`);
console.log(` Taux de succès: ${successRate}%`);
// 7. Utilisation de clés dérivées
console.log('\n🔐 Test de clés dérivées...');
const password = 'mon-mot-de-passe-secret';
const derivedKey = VaultCrypto.hashToKey(password);
console.log(` Mot de passe: ${password}`);
console.log(` Clé dérivée: ${derivedKey.substring(0, 10)}...`);
console.log(` Clé valide: ${VaultCrypto.validateKey(derivedKey) ? 'Oui' : 'Non'}`);
console.log('\n🎉 Exemple avancé terminé avec succès!');
} catch (error) {
console.error('❌ Erreur fatale:', error instanceof Error ? error.message : error);
if (error instanceof VaultApiError) {
console.error(` Code d'erreur: ${error.statusCode}`);
console.error(` Endpoint: ${error.endpoint}`);
} else if (error instanceof VaultDecryptionError) {
console.error(' Type: Erreur de déchiffrement');
}
process.exit(1);
}
}
// Fonction utilitaire pour les tests de performance
async function performanceTest(client: VaultClient, iterations: number = 10) {
console.log(`\n🏃 Test de performance (${iterations} itérations)...`);
const times: number[] = [];
for (let i = 0; i < iterations; i++) {
const start = Date.now();
await client.getFile('dev', 'bitcoin/bitcoin.conf');
const end = Date.now();
times.push(end - start);
}
const avgTime = times.reduce((sum, time) => sum + time, 0) / times.length;
const minTime = Math.min(...times);
const maxTime = Math.max(...times);
console.log(` Temps moyen: ${Math.round(avgTime)}ms`);
console.log(` Temps min: ${minTime}ms`);
console.log(` Temps max: ${maxTime}ms`);
return { avgTime, minTime, maxTime };
}
// Exécution de l'exemple
if (require.main === module) {
advancedExample();
}
export { advancedExample, performanceTest };

View File

@ -0,0 +1,79 @@
/**
* Exemple d'utilisation basique du SDK Vault 4NK
*/
import { VaultClient, createVaultClient, VaultCrypto } from '../src/index';
async function basicExample() {
console.log('🚀 Exemple d\'utilisation basique du SDK Vault 4NK');
console.log('=' .repeat(50));
try {
// 1. Création du client avec la clé de déchiffrement
const client = createVaultClient(
'https://vault.4nkweb.com:6666',
'quantum_resistant_demo_key_32_bytes!' // Clé de démonstration
);
// 2. Vérification de la connectivité
console.log('🔍 Test de connectivité...');
const isConnected = await client.ping();
console.log(`✅ Connecté: ${isConnected ? 'Oui' : 'Non'}`);
if (!isConnected) {
throw new Error('Impossible de se connecter à l\'API Vault');
}
// 3. Informations sur l'API
console.log('\n📋 Informations API...');
const info = await client.info();
console.log(` Nom: ${info.name}`);
console.log(` Version: ${info.version}`);
console.log(` Domaine: ${info.domain}`);
console.log(` Port: ${info.port}`);
console.log(` Chiffrement: ${info.encryption}`);
// 4. État de santé
console.log('\n🏥 État de santé...');
const health = await client.health();
console.log(` Statut: ${health.status}`);
console.log(` Service: ${health.service}`);
console.log(` Algorithme: ${health.algorithm}`);
// 5. Récupération d'un fichier
console.log('\n📁 Récupération d\'un fichier...');
const file = await client.getFile('dev', 'bitcoin/bitcoin.conf');
console.log(` Fichier: ${file.filename}`);
console.log(` Taille: ${file.size} caractères`);
console.log(` Chiffré: ${file.encrypted ? 'Oui' : 'Non'}`);
console.log(` Algorithme: ${file.algorithm || 'N/A'}`);
// Affichage d'un échantillon du contenu
const sample = file.content.substring(0, 200);
console.log(` Échantillon: ${sample}...`);
// 6. Récupération de plusieurs fichiers
console.log('\n📚 Récupération de plusieurs fichiers...');
const files = await client.getFiles([
{ env: 'dev', filePath: 'tor/torrc' },
{ env: 'dev', filePath: 'sdk_relay/sdk_relay.conf' }
]);
files.forEach((file, index) => {
console.log(` Fichier ${index + 1}: ${file.filename} (${file.size} chars)`);
});
console.log('\n🎉 Exemple terminé avec succès!');
} catch (error) {
console.error('❌ Erreur:', error instanceof Error ? error.message : error);
process.exit(1);
}
}
// Exécution de l'exemple
if (require.main === module) {
basicExample();
}
export { basicExample };

View File

@ -0,0 +1,240 @@
/**
* Exemple de gestion d'erreurs avancée avec le SDK Vault 4NK
* Démontre les différents types d'erreurs et leur gestion
*/
import { VaultClient, VaultApiError, VaultDecryptionError, VaultCrypto } from '../src/index';
async function errorHandlingExample() {
console.log('🛡️ Exemple de gestion d\'erreurs - SDK Vault 4NK');
console.log('=' .repeat(55));
try {
const client = new VaultClient(
{ baseUrl: 'https://vault.4nkweb.com:6666' },
'quantum_resistant_demo_key_32_bytes!'
);
// 1. Test des erreurs de connectivité
console.log('🌐 Test des erreurs de connectivité...');
await testConnectivityErrors(client);
// 2. Test des erreurs de fichiers
console.log('\n📁 Test des erreurs de fichiers...');
await testFileErrors(client);
// 3. Test des erreurs de déchiffrement
console.log('\n🔐 Test des erreurs de déchiffrement...');
await testDecryptionErrors();
// 4. Test des erreurs de configuration
console.log('\n⚙ Test des erreurs de configuration...');
await testConfigurationErrors();
// 5. Gestion d'erreurs avec retry
console.log('\n🔄 Test de retry automatique...');
await testRetryLogic(client);
console.log('\n✅ Tous les tests de gestion d\'erreurs terminés!');
} catch (error) {
console.error('❌ Erreur inattendue:', error);
process.exit(1);
}
}
async function testConnectivityErrors(client: VaultClient) {
// Test avec une URL incorrecte
const badClient = new VaultClient(
{ baseUrl: 'https://api-inexistante.example.com:6666' },
'quantum_resistant_demo_key_32_bytes!'
);
try {
await badClient.health();
console.log(' ❌ Erreur: Devrait échouer');
} catch (error) {
if (error instanceof VaultApiError) {
console.log(` ✅ Erreur de connectivité capturée: ${error.message}`);
} else {
console.log(` ✅ Erreur réseau capturée: ${error instanceof Error ? error.message : error}`);
}
}
// Test de timeout
const timeoutClient = new VaultClient(
{ baseUrl: 'https://vault.4nkweb.com:6666', timeout: 1 }, // 1ms timeout
'quantum_resistant_demo_key_32_bytes!'
);
try {
await timeoutClient.health();
console.log(' ❌ Erreur: Devrait timeout');
} catch (error) {
if (error instanceof VaultApiError && error.statusCode === 408) {
console.log(` ✅ Timeout capturé: ${error.message}`);
} else {
console.log(` ✅ Erreur timeout: ${error instanceof Error ? error.message : error}`);
}
}
}
async function testFileErrors(client: VaultClient) {
// Test avec un fichier inexistant
try {
await client.getFile('dev', 'fichier-inexistant.txt');
console.log(' ❌ Erreur: Fichier inexistant devrait échouer');
} catch (error) {
if (error instanceof VaultApiError && error.statusCode === 404) {
console.log(` ✅ Fichier inexistant: ${error.message}`);
} else {
console.log(` ✅ Erreur fichier: ${error instanceof Error ? error.message : error}`);
}
}
// Test avec un environnement inexistant
try {
await client.getFile('prod', 'bitcoin/bitcoin.conf');
console.log(' ❌ Erreur: Environnement inexistant devrait échouer');
} catch (error) {
if (error instanceof VaultApiError) {
console.log(` ✅ Environnement inexistant: ${error.message} (${error.statusCode})`);
} else {
console.log(` ✅ Erreur environnement: ${error instanceof Error ? error.message : error}`);
}
}
// Test avec un chemin invalide (tentative d'accès hors du répertoire)
try {
await client.getFile('dev', '../../../etc/passwd');
console.log(' ❌ Erreur: Chemin invalide devrait échouer');
} catch (error) {
if (error instanceof VaultApiError && error.statusCode === 403) {
console.log(` ✅ Chemin invalide (sécurité): ${error.message}`);
} else {
console.log(` ✅ Erreur sécurité: ${error instanceof Error ? error.message : error}`);
}
}
}
async function testDecryptionErrors() {
// Test avec une clé de déchiffrement incorrecte
const badKeyClient = new VaultClient(
{ baseUrl: 'https://vault.4nkweb.com:6666' },
'clé-incorrecte-de-32-bytes!' // Clé différente
);
try {
await badKeyClient.getFile('dev', 'bitcoin/bitcoin.conf');
console.log(' ❌ Erreur: Clé incorrecte devrait échouer');
} catch (error) {
if (error instanceof VaultDecryptionError) {
console.log(` ✅ Erreur de déchiffrement: ${error.message}`);
} else {
console.log(` ✅ Erreur clé: ${error instanceof Error ? error.message : error}`);
}
}
// Test avec une clé de taille incorrecte
try {
new VaultClient(
{ baseUrl: 'https://vault.4nkweb.com:6666' },
'clé-trop-courte' // Moins de 32 bytes
);
console.log(' ❌ Erreur: Clé trop courte devrait échouer');
} catch (error) {
console.log(` ✅ Clé invalide: ${error instanceof Error ? error.message : error}`);
}
}
async function testConfigurationErrors() {
// Test avec une URL malformée
try {
new VaultClient(
{ baseUrl: 'url-malformée' },
'quantum_resistant_demo_key_32_bytes!'
);
console.log(' ❌ Erreur: URL malformée devrait échouer');
} catch (error) {
console.log(` ✅ URL malformée: ${error instanceof Error ? error.message : error}`);
}
// Test avec un port invalide
try {
new VaultClient(
{ baseUrl: 'https://vault.4nkweb.com:99999' }, // Port invalide
'quantum_resistant_demo_key_32_bytes!'
);
console.log(' ❌ Erreur: Port invalide devrait échouer');
} catch (error) {
console.log(` ✅ Port invalide: ${error instanceof Error ? error.message : error}`);
}
}
async function testRetryLogic(client: VaultClient) {
// Implémentation d'un retry simple
async function retryOperation<T>(
operation: () => Promise<T>,
maxRetries: number = 3,
delay: number = 1000
): Promise<T> {
let lastError: Error;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
lastError = error as Error;
console.log(` 🔄 Tentative ${attempt}/${maxRetries} échouée: ${lastError.message}`);
if (attempt < maxRetries) {
console.log(` ⏳ Attente de ${delay}ms avant retry...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
throw lastError!;
}
// Test de retry avec une opération qui peut échouer
try {
const result = await retryOperation(
() => client.getFile('dev', 'bitcoin/bitcoin.conf'),
3,
500
);
console.log(` ✅ Retry réussi: ${result.filename} (${result.size} chars)`);
} catch (error) {
console.log(` ❌ Retry échoué après toutes les tentatives: ${error instanceof Error ? error.message : error}`);
}
}
// Fonction utilitaire pour logger les erreurs
function logError(error: unknown, context: string) {
console.log(`\n🚨 Erreur dans ${context}:`);
if (error instanceof VaultApiError) {
console.log(` Type: VaultApiError`);
console.log(` Message: ${error.message}`);
console.log(` Code: ${error.statusCode}`);
console.log(` Endpoint: ${error.endpoint}`);
} else if (error instanceof VaultDecryptionError) {
console.log(` Type: VaultDecryptionError`);
console.log(` Message: ${error.message}`);
} else if (error instanceof Error) {
console.log(` Type: ${error.constructor.name}`);
console.log(` Message: ${error.message}`);
console.log(` Stack: ${error.stack?.split('\n').slice(0, 3).join('\n')}`);
} else {
console.log(` Type: Inconnu`);
console.log(` Valeur: ${error}`);
}
}
// Exécution de l'exemple
if (require.main === module) {
errorHandlingExample();
}
export { errorHandlingExample, logError };

22
sdk-client/jest.config.js Normal file
View File

@ -0,0 +1,22 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/src', '<rootDir>/examples'],
testMatch: [
'**/__tests__/**/*.ts',
'**/?(*.)+(spec|test).ts'
],
transform: {
'^.+\\.ts$': 'ts-jest',
},
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.d.ts',
'!examples/**/*.ts'
],
coverageDirectory: 'coverage',
coverageReporters: ['text', 'lcov', 'html'],
setupFilesAfterEnv: ['<rootDir>/src/__tests__/setup.ts'],
testTimeout: 30000,
verbose: true
};

5268
sdk-client/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

59
sdk-client/package.json Normal file
View File

@ -0,0 +1,59 @@
{
"name": "@4nk/vault-sdk",
"version": "1.0.0",
"description": "SDK TypeScript pour l'API Vault 4NK - Service de fichiers chiffrés quantique résistant",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"test": "jest",
"lint": "eslint src/**/*.ts",
"clean": "rm -rf dist"
},
"keywords": [
"vault",
"4nk",
"api",
"client",
"typescript",
"quantum-resistant",
"encryption",
"chacha20",
"poly1305"
],
"author": "4NK Team",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://git.4nkweb.com/4nk/vault-sdk.git"
},
"bugs": {
"url": "https://git.4nkweb.com/4nk/vault-sdk/issues"
},
"homepage": "https://git.4nkweb.com/4nk/vault-sdk#readme",
"devDependencies": {
"@types/jest": "^29.5.8",
"@types/node": "^20.10.0",
"@typescript-eslint/eslint-plugin": "^6.13.0",
"@typescript-eslint/parser": "^6.13.0",
"eslint": "^8.54.0",
"jest": "^29.7.0",
"ts-jest": "^29.1.1",
"typescript": "^5.3.0"
},
"dependencies": {
"node-fetch": "^3.3.2"
},
"peerDependencies": {
"typescript": ">=4.0.0"
},
"engines": {
"node": ">=16.0.0"
},
"files": [
"dist/**/*",
"README.md",
"LICENSE"
]
}

25
sdk-client/simple-test.js Normal file
View File

@ -0,0 +1,25 @@
const fetch = require('node-fetch');
async function test() {
try {
console.log('Test simple de l\'API...');
const response = await fetch('https://127.0.0.1:6666/health', {
method: 'GET',
headers: {
'Accept': 'application/json',
}
});
if (response.ok) {
const data = await response.json();
console.log('✅ API fonctionne:', data);
} else {
console.log('❌ Erreur HTTP:', response.status, response.statusText);
}
} catch (error) {
console.log('❌ Erreur:', error.message);
}
}
test();

View File

@ -0,0 +1,29 @@
/**
* Configuration des tests pour le SDK Vault Client
*/
// Configuration globale pour les tests
beforeAll(() => {
// Configuration des timeouts pour les tests
jest.setTimeout(30000);
});
beforeEach(() => {
// Reset des mocks avant chaque test
jest.clearAllMocks();
});
afterEach(() => {
// Nettoyage après chaque test
jest.restoreAllMocks();
});
// Mock des modules Node.js si nécessaire
jest.mock('crypto', () => ({
...jest.requireActual('crypto'),
createDecipher: jest.fn(),
}));
// Variables d'environnement pour les tests
process.env['NODE_ENV'] = 'test';
process.env['TZ'] = 'UTC';

View File

@ -0,0 +1,286 @@
/**
* Tests unitaires pour le SDK Vault Client
*/
import { VaultClient, VaultApiError, VaultDecryptionError, VaultCrypto } from '../index';
// Mock de fetch pour les tests
global.fetch = jest.fn();
describe('VaultClient', () => {
let client: VaultClient;
const mockDecryptionKey = 'quantum_resistant_demo_key_32_bytes!';
const mockBaseUrl = 'https://vault.4nkweb.com:6666';
beforeEach(() => {
client = new VaultClient({ baseUrl: mockBaseUrl }, mockDecryptionKey);
(fetch as jest.Mock).mockClear();
});
afterEach(() => {
jest.restoreAllMocks();
});
describe('Construction', () => {
it('devrait créer un client avec une configuration valide', () => {
expect(client).toBeInstanceOf(VaultClient);
});
it('devrait rejeter une clé de déchiffrement invalide', () => {
expect(() => {
new VaultClient({ baseUrl: mockBaseUrl }, 'clé-trop-courte');
}).toThrow('La clé de déchiffrement doit faire exactement 32 bytes');
});
});
describe('health()', () => {
it('devrait retourner les informations de santé', async () => {
const mockHealth = {
status: 'healthy',
service: 'vault-api',
encryption: 'quantum-resistant',
algorithm: 'X25519-ChaCha20-Poly1305'
};
(fetch as jest.Mock).mockResolvedValueOnce({
ok: true,
headers: { get: () => 'application/json' },
json: () => Promise.resolve(mockHealth)
});
const result = await client.health();
expect(result).toEqual(mockHealth);
expect(fetch).toHaveBeenCalledWith(
`${mockBaseUrl}/health`,
expect.objectContaining({
method: 'GET',
headers: expect.objectContaining({
'Accept': 'application/json, application/octet-stream'
})
})
);
});
it('devrait gérer les erreurs HTTP', async () => {
(fetch as jest.Mock).mockResolvedValueOnce({
ok: false,
status: 500,
statusText: 'Internal Server Error'
});
await expect(client.health()).rejects.toThrow(VaultApiError);
});
});
describe('info()', () => {
it('devrait retourner les informations de l\'API', async () => {
const mockInfo = {
name: '4NK Vault API',
version: '1.0.0',
domain: 'vault.4nkweb.com',
port: 6666,
protocol: 'HTTPS',
encryption: 'quantum-resistant',
endpoints: {}
};
(fetch as jest.Mock).mockResolvedValueOnce({
ok: true,
headers: { get: () => 'application/json' },
json: () => Promise.resolve(mockInfo)
});
const result = await client.info();
expect(result).toEqual(mockInfo);
});
});
describe('ping()', () => {
it('devrait retourner true si l\'API répond', async () => {
(fetch as jest.Mock).mockResolvedValueOnce({
ok: true,
headers: { get: () => 'application/json' },
json: () => Promise.resolve({ status: 'healthy' })
});
const result = await client.ping();
expect(result).toBe(true);
});
it('devrait retourner false si l\'API ne répond pas', async () => {
(fetch as jest.Mock).mockRejectedValueOnce(new Error('Network error'));
const result = await client.ping();
expect(result).toBe(false);
});
});
describe('getFile()', () => {
it('devrait récupérer et déchiffrer un fichier', async () => {
// Mock du contenu chiffré (base64)
const mockEncryptedContent = Buffer.from('contenu-test-chiffré');
const mockBase64Content = mockEncryptedContent.toString('base64');
(fetch as jest.Mock).mockResolvedValueOnce({
ok: true,
headers: { get: (name: string) => {
if (name === 'content-type') return 'application/octet-stream';
if (name === 'x-encryption-type') return 'quantum-resistant';
return null;
}},
arrayBuffer: () => Promise.resolve(mockEncryptedContent.buffer)
});
// Mock du déchiffrement
const mockDecryptedContent = 'contenu-test-déchiffré';
jest.spyOn(client as any, 'decryptContent').mockReturnValue(mockDecryptedContent);
const result = await client.getFile('dev', 'test.conf');
expect(result).toEqual({
content: mockDecryptedContent,
filename: 'test.conf',
size: mockDecryptedContent.length,
encrypted: true,
algorithm: 'ChaCha20-Poly1305'
});
});
it('devrait gérer les fichiers non trouvés', async () => {
(fetch as jest.Mock).mockResolvedValueOnce({
ok: false,
status: 404,
statusText: 'Not Found'
});
await expect(client.getFile('dev', 'fichier-inexistant.conf'))
.rejects
.toThrow(VaultApiError);
});
it('devrait gérer les erreurs de déchiffrement', async () => {
const mockEncryptedContent = Buffer.from('contenu-invalide');
(fetch as jest.Mock).mockResolvedValueOnce({
ok: true,
headers: { get: () => 'application/octet-stream' },
arrayBuffer: () => Promise.resolve(mockEncryptedContent.buffer)
});
jest.spyOn(client as any, 'decryptContent').mockImplementation(() => {
throw new Error('Erreur de déchiffrement');
});
await expect(client.getFile('dev', 'test.conf'))
.rejects
.toThrow(VaultDecryptionError);
});
});
describe('getFiles()', () => {
it('devrait récupérer plusieurs fichiers en parallèle', async () => {
const mockFile1 = {
content: 'contenu1',
filename: 'file1.conf',
size: 8,
encrypted: true,
algorithm: 'ChaCha20-Poly1305'
};
const mockFile2 = {
content: 'contenu2',
filename: 'file2.conf',
size: 8,
encrypted: true,
algorithm: 'ChaCha20-Poly1305'
};
jest.spyOn(client, 'getFile')
.mockResolvedValueOnce(mockFile1)
.mockResolvedValueOnce(mockFile2);
const requests = [
{ env: 'dev', filePath: 'file1.conf' },
{ env: 'dev', filePath: 'file2.conf' }
];
const result = await client.getFiles(requests);
expect(result).toEqual([mockFile1, mockFile2]);
expect(client.getFile).toHaveBeenCalledTimes(2);
});
});
});
describe('VaultCrypto', () => {
describe('generateKey()', () => {
it('devrait générer une clé de 32 bytes', () => {
const key = VaultCrypto.generateKey();
expect(Buffer.byteLength(key, 'utf8')).toBe(32);
});
it('devrait générer des clés différentes à chaque appel', () => {
const key1 = VaultCrypto.generateKey();
const key2 = VaultCrypto.generateKey();
expect(key1).not.toBe(key2);
});
});
describe('validateKey()', () => {
it('devrait valider une clé de 32 bytes', () => {
const validKey = 'a'.repeat(32);
expect(VaultCrypto.validateKey(validKey)).toBe(true);
});
it('devrait rejeter une clé trop courte', () => {
const shortKey = 'a'.repeat(31);
expect(VaultCrypto.validateKey(shortKey)).toBe(false);
});
it('devrait rejeter une clé trop longue', () => {
const longKey = 'a'.repeat(33);
expect(VaultCrypto.validateKey(longKey)).toBe(false);
});
});
describe('hashToKey()', () => {
it('devrait générer une clé de 32 bytes depuis un mot de passe', () => {
const password = 'mon-mot-de-passe';
const key = VaultCrypto.hashToKey(password);
expect(Buffer.byteLength(key, 'utf8')).toBe(32);
});
it('devrait générer la même clé pour le même mot de passe', () => {
const password = 'mot-de-passe-test';
const key1 = VaultCrypto.hashToKey(password);
const key2 = VaultCrypto.hashToKey(password);
expect(key1).toBe(key2);
});
it('devrait générer des clés différentes pour des mots de passe différents', () => {
const key1 = VaultCrypto.hashToKey('mot-de-passe-1');
const key2 = VaultCrypto.hashToKey('mot-de-passe-2');
expect(key1).not.toBe(key2);
});
});
});
describe('Classes d\'erreurs', () => {
describe('VaultApiError', () => {
it('devrait créer une erreur API avec les bonnes propriétés', () => {
const error = new VaultApiError('Test error', 404, '/test');
expect(error.message).toBe('Test error');
expect(error.statusCode).toBe(404);
expect(error.endpoint).toBe('/test');
expect(error.name).toBe('VaultApiError');
});
});
describe('VaultDecryptionError', () => {
it('devrait créer une erreur de déchiffrement', () => {
const error = new VaultDecryptionError('Decryption failed');
expect(error.message).toBe('Decryption failed');
expect(error.name).toBe('VaultDecryptionError');
});
});
});

288
sdk-client/src/index.ts Normal file
View File

@ -0,0 +1,288 @@
/**
* SDK Client TypeScript pour l'API Vault 4NK
* Permet de récupérer et déchiffrer les fichiers depuis l'API Vault
*/
import { createDecipher } from 'crypto';
import fetch from 'node-fetch';
// Types TypeScript
export interface VaultConfig {
baseUrl: string;
verifySsl?: boolean;
timeout?: number;
}
export interface VaultFile {
content: string;
filename: string;
size: number;
encrypted: boolean;
algorithm?: string | undefined;
}
export interface VaultHealth {
status: string;
service: string;
encryption: string;
algorithm: string;
}
export interface VaultInfo {
name: string;
version: string;
domain: string;
port: number;
protocol: string;
encryption: string;
endpoints: Record<string, string>;
}
export class VaultApiError extends Error {
constructor(
message: string,
public statusCode?: number,
public endpoint?: string
) {
super(message);
this.name = 'VaultApiError';
}
}
export class VaultDecryptionError extends Error {
constructor(message: string) {
super(message);
this.name = 'VaultDecryptionError';
}
}
/**
* Client principal pour l'API Vault 4NK
*/
export class VaultClient {
private config: VaultConfig;
private decryptionKey: Buffer;
constructor(config: VaultConfig, decryptionKey: string) {
this.config = {
verifySsl: false,
timeout: 30000,
...config,
};
// Validation de la clé de déchiffrement (32 bytes)
const keyLength = Buffer.byteLength(decryptionKey, 'utf8');
if (keyLength !== 32) {
throw new Error(`La clé de déchiffrement doit faire exactement 32 bytes (reçu: ${keyLength} bytes)`);
}
this.decryptionKey = Buffer.from(decryptionKey, 'utf8');
}
/**
* Effectue une requête HTTPS vers l'API Vault
*/
private async makeRequest<T>(
endpoint: string,
options: RequestInit = {}
): Promise<T> {
const url = `${this.config.baseUrl}${endpoint}`;
const defaultOptions: RequestInit = {
method: 'GET',
headers: {
'Accept': 'application/json, application/octet-stream',
'User-Agent': 'VaultSDK-TypeScript/1.0.0',
},
signal: AbortSignal.timeout(this.config.timeout!),
};
const mergedOptions = { ...defaultOptions, ...options } as any;
try {
const response = await fetch(url, mergedOptions);
if (!response.ok) {
throw new VaultApiError(
`Erreur API: ${response.status} ${response.statusText}`,
response.status,
endpoint
);
}
const contentType = response.headers.get('content-type');
if (contentType?.includes('application/json')) {
return await response.json() as T;
} else {
// Pour les fichiers chiffrés, retourner le buffer
const arrayBuffer = await response.arrayBuffer();
return Buffer.from(arrayBuffer) as unknown as T;
}
} catch (error) {
if (error instanceof VaultApiError) {
throw error;
}
if (error instanceof Error) {
if (error.name === 'AbortError') {
throw new VaultApiError('Timeout de la requête', 408, endpoint);
}
throw new VaultApiError(`Erreur réseau: ${error.message}`, undefined, endpoint);
}
throw new VaultApiError('Erreur inconnue', undefined, endpoint);
}
}
/**
* Déchiffre le contenu d'un fichier
*/
private decryptContent(encryptedContent: Buffer): string {
try {
// Décoder le base64
const decoded = Buffer.from(encryptedContent.toString(), 'base64');
// Extraire le nonce (12 premiers bytes)
const nonce = decoded.subarray(0, 12);
const ciphertext = decoded.subarray(12);
// Déchiffrer avec ChaCha20-Poly1305
const decipher = createDecipher('chacha20-poly1305', this.decryptionKey, nonce);
let decrypted = decipher.update(ciphertext, undefined, 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
} catch (error) {
throw new VaultDecryptionError(
`Erreur de déchiffrement: ${error instanceof Error ? error.message : 'Inconnue'}`
);
}
}
/**
* Récupère un fichier depuis l'API Vault
*/
async getFile(env: string, filePath: string): Promise<VaultFile> {
const endpoint = `/${env}/${filePath}`;
try {
const encryptedContent = await this.makeRequest<Buffer>(endpoint);
// Vérifier si le contenu est en base64 (chiffré)
let content: string;
let encrypted = true;
try {
content = this.decryptContent(encryptedContent);
} catch (error) {
// Si le déchiffrement échoue, essayer de décoder en base64 simple
try {
content = Buffer.from(encryptedContent.toString(), 'base64').toString('utf8');
encrypted = false;
} catch {
throw new VaultDecryptionError('Impossible de déchiffrer ou décoder le contenu');
}
}
return {
content,
filename: filePath.split('/').pop() || filePath,
size: content.length,
encrypted,
algorithm: encrypted ? 'ChaCha20-Poly1305' : undefined,
};
} catch (error) {
if (error instanceof VaultApiError && error.statusCode === 404) {
throw new VaultApiError(`Fichier non trouvé: ${env}/${filePath}`, 404, endpoint);
}
throw error;
}
}
/**
* Vérifie l'état de santé de l'API
*/
async health(): Promise<VaultHealth> {
return await this.makeRequest<VaultHealth>('/health');
}
/**
* Récupère les informations de l'API
*/
async info(): Promise<VaultInfo> {
return await this.makeRequest<VaultInfo>('/info');
}
/**
* Teste la connectivité à l'API
*/
async ping(): Promise<boolean> {
try {
await this.health();
return true;
} catch {
return false;
}
}
/**
* Récupère plusieurs fichiers en parallèle
*/
async getFiles(requests: Array<{ env: string; filePath: string }>): Promise<VaultFile[]> {
const promises = requests.map(req => this.getFile(req.env, req.filePath));
return await Promise.all(promises);
}
/**
* Recherche des fichiers par pattern dans un environnement
*/
async searchFiles(_env: string, _pattern?: RegExp): Promise<string[]> {
// Note: Cette méthode nécessiterait un endpoint de recherche côté serveur
// Pour l'instant, retourne une liste vide
console.warn('La recherche de fichiers n\'est pas encore implémentée côté serveur');
return [];
}
}
/**
* Factory pour créer une instance du client Vault
*/
export function createVaultClient(
baseUrl: string = 'https://vault.4nkweb.com:6666',
decryptionKey: string = 'quantum_resistant_demo_key_32_bytes!'
): VaultClient {
return new VaultClient({ baseUrl }, decryptionKey);
}
/**
* Utilitaires pour la gestion des clés de chiffrement
*/
export class VaultCrypto {
/**
* Génère une clé de déchiffrement aléatoire (32 bytes)
*/
static generateKey(): string {
const crypto = require('crypto');
return crypto.randomBytes(32).toString('utf8');
}
/**
* Vérifie qu'une clé est valide (32 bytes)
*/
static validateKey(key: string): boolean {
return Buffer.byteLength(key, 'utf8') === 32;
}
/**
* Hache une clé pour générer une clé de 32 bytes
*/
static hashToKey(password: string): string {
const crypto = require('crypto');
return crypto.createHash('sha256').update(password).digest('utf8');
}
}
// Export par défaut
export default VaultClient;

41
sdk-client/tsconfig.json Normal file
View File

@ -0,0 +1,41 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020", "DOM"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"removeComments": false,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"exactOptionalPropertyTypes": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noUncheckedIndexedAccess": true,
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"isolatedModules": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"dist",
"**/*.test.ts",
"**/*.spec.ts"
]
}

41
start_api.sh Executable file
View File

@ -0,0 +1,41 @@
#!/bin/bash
# Script de démarrage de l'API Vault
# Port 6666, domaine vault.4nkweb.com
echo "🚀 Démarrage de l'API Vault 4NK..."
echo "📍 Domaine: vault.4nkweb.com"
echo "🔌 Port: 6666"
echo "🔐 Chiffrement: Quantique résistant (X25519 + ChaCha20-Poly1305)"
echo "📁 Répertoire de stockage: /home/debian/4NK_vault/storage"
echo ""
# Vérification des dépendances Python
echo "🔍 Vérification des dépendances..."
python3 -c "import flask, cryptography" 2>/dev/null
if [ $? -ne 0 ]; then
echo "📦 Installation des dépendances..."
pip3 install -r requirements.txt
fi
# Vérification du répertoire de stockage
if [ ! -d "/home/debian/4NK_vault/storage" ]; then
echo "❌ Erreur: Le répertoire de stockage n'existe pas"
exit 1
fi
# Vérification du fichier .env
if [ ! -f "/home/debian/4NK_vault/storage/dev/.env" ]; then
echo "⚠️ Avertissement: Fichier .env non trouvé dans storage/dev/"
fi
echo "✅ Démarrage de l'API..."
echo "🌐 URL: https://vault.4nkweb.com:6666"
echo "📋 Endpoints disponibles:"
echo " GET /<env>/<file> - Sert un fichier chiffré"
echo " GET /health - Contrôle de santé"
echo " GET /info - Informations API"
echo ""
# Démarrage de l'API
python3 api_server.py

0
storage/.gitkeep Normal file
View File

0
storage/dev/.gitkeep Normal file
View File

View File

View File

@ -0,0 +1,45 @@
# Configuration globale
signet=1
server=1
datadir=$ROOT_DIR_LOGS/bitcoin
[signet]
daemon=0
txindex=1
upnp=1
#debug=1
#loglevel=debug
logthreadnames=1
onion=tor:$TOR_PORT
listenonion=1
onlynet=onion
# Paramètres RPC
rpcauth=$BITCOIN_RPC_AUTH
rpcallowip=0.0.0.0/0
rpcworkqueue=32
rpcthreads=4
rpcdoccheck=1
# Paramètres ZMQ
zmqpubhashblock=tcp://:$BITCOIN_ZMQPBUBHASHBLOCK_PORT
zmqpubrawtx=tcp://:$BITCOIN_ZMQPUBRAWTX_PORT
listen=1
bind=:$BITCOIN_SIGNET_P2P_PORT
rpcbind=:$BITCOIN_SIGNET_RPC_PORT
rpcport=$BITCOIN_SIGNET_RPC_PORT
fallbackfee=0.0001
blockfilterindex=1
datacarriersize=205
acceptnonstdtxn=1
dustrelayfee=0.00000001
minrelaytxfee=0.00000001
prune=0
signetchallenge=0020341c43803863c252df326e73574a27d7e19322992061017b0dc893e2eab90821
wallet=$BITCOIN_WALLET_NAME
wallet=watchonly
maxtxfee=1
addnode=tlv2yqamflv22vfdzy2hha2nwmt6zrwrhjjzz4lx7qyq7lyc6wfhabyd.onion
addnode=6xi33lwwslsx3yi3f7c56wnqtdx4v73vj2up3prrwebpwbz6qisnqbyd.onion
addnode=id7e3r3d2epen2v65jebjhmx77aimu7oyhcg45zadafypr4crqsytfid.onion

View File

View File

@ -0,0 +1,18 @@
# Configuration Blindbit Oracle
host = "0.0.0.0:$BLINDBIT_PORT"
chain = "signet"
rpc_endpoint = "$BITCOIN_RPC_URL"
cookie_path = "$BITCOIN_COOKIE_PATH"
rpc_user = ""
rpc_pass = ""
sync_start_height = 1
# Performance
max_parallel_tweak_computations = 4
max_parallel_requests = 4
# Index
tweaks_only = 0
tweaks_full_basic = 1
tweaks_full_with_dust_filter = 1
tweaks_cut_through_with_dust_filter = 1

0
storage/dev/git/.gitkeep Normal file
View File

View File

View File

View File

@ -0,0 +1,399 @@
{
"annotations": {
"list": []
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": null,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"vis": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 1,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltip": {
"mode": "single"
}
},
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "sum(rate({container=\"signet_miner\"} |= \"Block mined\" [5m])) by (container)",
"queryType": "",
"refId": "A"
}
],
"title": "Blocs Minés par Minute",
"type": "timeseries"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"vis": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 2,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltip": {
"mode": "single"
}
},
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "sum(rate({container=\"signet_miner\"} |= \"Hashrate\" [5m])) by (container)",
"queryType": "",
"refId": "A"
}
],
"title": "Hashrate du Mineur",
"type": "timeseries"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 8,
"x": 0,
"y": 8
},
"id": 3,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "count_over_time({container=\"signet_miner\"} |= \"ERROR\" [1h])",
"queryType": "",
"refId": "A"
}
],
"title": "Erreurs du Mineur (1h)",
"type": "stat"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"hideFrom": {
"legend": false,
"tooltip": false,
"vis": false
}
},
"mappings": []
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 16,
"x": 8,
"y": 8
},
"id": 4,
"options": {
"legend": {
"displayMode": "list",
"placement": "right"
},
"pieType": "pie",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"tooltip": {
"mode": "single"
}
},
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "sum by (level) (count_over_time({container=\"signet_miner\"} | json | level != \"\" [1h]))",
"queryType": "",
"refId": "A"
}
],
"title": "Distribution des Niveaux de Log",
"type": "piechart"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"custom": {
"align": "auto",
"cellOptions": {
"type": "auto"
},
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 24,
"x": 0,
"y": 16
},
"id": 5,
"options": {
"cellHeight": "sm",
"footer": {
"countRows": false,
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "{container=\"signet_miner\"} |= \"Block mined\" | json | line_format \"{{.timestamp}} - Bloc {{.height}} miné - Hash: {{.hash}}\"",
"queryType": "",
"refId": "A"
}
],
"title": "Historique des Blocs Minés",
"type": "table"
}
],
"refresh": "5s",
"schemaVersion": 37,
"style": "dark",
"tags": [
"bitcoin",
"miner",
"signet"
],
"templating": {
"list": []
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Bitcoin Miner - Détails",
"uid": "bitcoin-miner-detailed",
"version": 1,
"weekStart": ""
}

View File

@ -0,0 +1,160 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": null,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 1,
"options": {
"showTime": false,
"showLabels": false,
"showCommonLabels": false,
"wrapLogMessage": false,
"prettifyLogMessage": false,
"enableLogDetails": true,
"dedupStrategy": "none",
"sortOrder": "Descending"
},
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "{job=\"bitcoin\"} |= \"block\" | logfmt",
"queryType": "",
"refId": "A"
}
],
"title": "Bitcoin - Nouveaux Blocs",
"type": "logs"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 2,
"options": {
"showTime": false,
"showLabels": false,
"showCommonLabels": false,
"wrapLogMessage": false,
"prettifyLogMessage": false,
"enableLogDetails": true,
"dedupStrategy": "none",
"sortOrder": "Descending"
},
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "{job=\"miner\"} |= \"mined\" | logfmt",
"queryType": "",
"refId": "A"
}
],
"title": "Miner - Blocs Minés",
"type": "logs"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"gridPos": {
"h": 8,
"w": 24,
"x": 0,
"y": 8
},
"id": 3,
"options": {
"showTime": false,
"showLabels": false,
"showCommonLabels": false,
"wrapLogMessage": false,
"prettifyLogMessage": false,
"enableLogDetails": true,
"dedupStrategy": "none",
"sortOrder": "Descending"
},
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "{job=~\"bitcoin|miner|blindbit\"} |= \"error\" | logfmt",
"queryType": "",
"refId": "A"
}
],
"title": "Bitcoin/Miner/Blindbit - Erreurs",
"type": "logs"
}
],
"refresh": "30s",
"schemaVersion": 36,
"style": "dark",
"tags": ["bitcoin", "miner", "blockchain"],
"templating": {
"list": []
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Bitcoin & Miner Monitoring",
"uid": "bitcoin-miner",
"version": 1,
"weekStart": ""
}

View File

@ -0,0 +1,532 @@
{
"annotations": {
"list": []
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": null,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"vis": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 1,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltip": {
"mode": "single"
}
},
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "sum(rate({container=\"bitcoin-signet\"} |= \"UpdateTip\" [5m])) by (container)",
"queryType": "",
"refId": "A"
}
],
"title": "Mises à Jour de la Chaîne Bitcoin",
"type": "timeseries"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"vis": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 2,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltip": {
"mode": "single"
}
},
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "sum(rate({container=\"blindbit-oracle\"} |= \"tweak\" [5m])) by (container)",
"queryType": "",
"refId": "A"
}
],
"title": "Détection de Tweak (BlindBit)",
"type": "timeseries"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 8
},
"id": 3,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "count_over_time({container=\"bitcoin-signet\"} |= \"ERROR\" [1h])",
"queryType": "",
"refId": "A"
}
],
"title": "Erreurs Bitcoin (1h)",
"type": "stat"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 8
},
"id": 4,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "count_over_time({container=\"blindbit-oracle\"} |= \"ERROR\" [1h])",
"queryType": "",
"refId": "A"
}
],
"title": "Erreurs BlindBit (1h)",
"type": "stat"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 6,
"x": 12,
"y": 8
},
"id": 5,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "count_over_time({container=\"bitcoin-signet\"} |= \"New block\" [1h])",
"queryType": "",
"refId": "A"
}
],
"title": "Nouveaux Blocs (1h)",
"type": "stat"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 6,
"x": 18,
"y": 8
},
"id": 6,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "count_over_time({container=\"blindbit-oracle\"} |= \"Silent payment\" [1h])",
"queryType": "",
"refId": "A"
}
],
"title": "Silent Payments (1h)",
"type": "stat"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"custom": {
"align": "auto",
"cellOptions": {
"type": "auto"
},
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 24,
"x": 0,
"y": 12
},
"id": 7,
"options": {
"cellHeight": "sm",
"footer": {
"countRows": false,
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "{container=~\"bitcoin-signet|blindbit-oracle\"} |= \"ERROR\" | line_format \"{{.timestamp}} - {{.container}} - {{.message}}\"",
"queryType": "",
"refId": "A"
}
],
"title": "Erreurs Bitcoin Services",
"type": "table"
}
],
"refresh": "5s",
"schemaVersion": 37,
"style": "dark",
"tags": [
"bitcoin",
"signet",
"blindbit",
"oracle"
],
"templating": {
"list": []
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Bitcoin Services - Monitoring",
"uid": "bitcoin-services",
"version": 1,
"weekStart": ""
}

View File

@ -0,0 +1,192 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"id": null,
"links": [],
"panels": [
{
"datasource": "Loki",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 1,
"options": {
"showLabels": false,
"showTime": false,
"sortOrder": "Descending",
"wrapLogMessage": false
},
"targets": [
{
"expr": "{job=\"blindbit\"} |= \"Host configuration loaded\"",
"refId": "A"
}
],
"title": "BlindBit Oracle - Configuration Loaded",
"type": "logs"
},
{
"datasource": "Loki",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 2,
"options": {
"showLabels": false,
"showTime": false,
"sortOrder": "Descending",
"wrapLogMessage": false
},
"targets": [
{
"expr": "{job=\"blindbit\"} |= \"Sync took\"",
"refId": "A"
}
],
"title": "BlindBit Oracle - Synchronization",
"type": "logs"
},
{
"datasource": "Loki",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 24,
"x": 0,
"y": 8
},
"id": 3,
"options": {
"showLabels": false,
"showTime": false,
"sortOrder": "Descending",
"wrapLogMessage": false
},
"targets": [
{
"expr": "{job=\"blindbit\"} |= \"successfully processed block\"",
"refId": "A"
}
],
"title": "BlindBit Oracle - Block Processing",
"type": "logs"
},
{
"datasource": "Loki",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 16
},
"id": 4,
"options": {
"showLabels": false,
"showTime": false,
"sortOrder": "Descending",
"wrapLogMessage": false
},
"targets": [
{
"expr": "{job=\"blindbit\"} |= \"GET\" |~ \"/tweaks/\"",
"refId": "A"
}
],
"title": "BlindBit Oracle - API Requests",
"type": "logs"
},
{
"datasource": "Loki",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 16
},
"id": 5,
"options": {
"showLabels": false,
"showTime": false,
"sortOrder": "Descending",
"wrapLogMessage": false
},
"targets": [
{
"expr": "{job=\"blindbit\"} |~ \"ERROR|error|Error\"",
"refId": "A"
}
],
"title": "BlindBit Oracle - Errors",
"type": "logs"
}
],
"schemaVersion": 27,
"style": "dark",
"tags": [
"blindbit",
"oracle",
"blockchain"
],
"templating": {
"list": []
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "BlindBit Oracle Dashboard",
"uid": "blindbit-oracle",
"version": 1
}

View File

@ -0,0 +1,532 @@
{
"annotations": {
"list": []
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": null,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"vis": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 1,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltip": {
"mode": "single"
}
},
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "sum(rate({container=~\"lecoffre-front|ihm_client\"} |= \"GET\" [5m])) by (container)",
"queryType": "",
"refId": "A"
}
],
"title": "Requêtes HTTP par Frontend",
"type": "timeseries"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"vis": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 2,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltip": {
"mode": "single"
}
},
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "sum(rate({container=\"ihm_client\"} |= \"vite\" [5m])) by (container)",
"queryType": "",
"refId": "A"
}
],
"title": "Activité Vite (IHM Client)",
"type": "timeseries"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 8
},
"id": 3,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "count_over_time({container=\"lecoffre-front\"} |= \"ERROR\" [1h])",
"queryType": "",
"refId": "A"
}
],
"title": "Erreurs LeCoffre Front (1h)",
"type": "stat"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 8
},
"id": 4,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "count_over_time({container=\"ihm_client\"} |= \"ERROR\" [1h])",
"queryType": "",
"refId": "A"
}
],
"title": "Erreurs IHM Client (1h)",
"type": "stat"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 6,
"x": 12,
"y": 8
},
"id": 5,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "sum(count_over_time({container=~\"lecoffre-front|ihm_client\"} [1h]))",
"queryType": "",
"refId": "A"
}
],
"title": "Total Logs Frontend (1h)",
"type": "stat"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 6,
"x": 18,
"y": 8
},
"id": 6,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "count_over_time({container=\"ihm_client\"} |= \"Pre-transform error\" [1h])",
"queryType": "",
"refId": "A"
}
],
"title": "Erreurs Vite (1h)",
"type": "stat"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"custom": {
"align": "auto",
"cellOptions": {
"type": "auto"
},
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 24,
"x": 0,
"y": 12
},
"id": 7,
"options": {
"cellHeight": "sm",
"footer": {
"countRows": false,
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "{container=~\"lecoffre-front|ihm_client\"} |= \"ERROR\" | line_format \"{{.timestamp}} - {{.container}} - {{.message}}\"",
"queryType": "",
"refId": "A"
}
],
"title": "Erreurs Récentes Frontend",
"type": "table"
}
],
"refresh": "5s",
"schemaVersion": 37,
"style": "dark",
"tags": [
"frontend",
"lecoffre",
"ihm",
"client"
],
"templating": {
"list": []
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Frontend Services - Monitoring",
"uid": "frontend-services",
"version": 1,
"weekStart": ""
}

View File

@ -0,0 +1,252 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": null,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"vis": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 1,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "sum by (service) (count_over_time({job=~\".*\"} |= \"error\" [5m]))",
"queryType": "",
"refId": "A"
}
],
"title": "Erreurs par Service (5 dernières minutes)",
"type": "timeseries"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"vis": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 2,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "sum by (service) (count_over_time({job=~\".*\"} [5m]))",
"queryType": "",
"refId": "A"
}
],
"title": "Volume de Logs par Service (5 dernières minutes)",
"type": "timeseries"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"gridPos": {
"h": 12,
"w": 24,
"x": 0,
"y": 8
},
"id": 3,
"options": {
"showTime": false,
"showLabels": false,
"showCommonLabels": false,
"wrapLogMessage": false,
"prettifyLogMessage": false,
"enableLogDetails": true,
"dedupStrategy": "none",
"sortOrder": "Descending"
},
"title": "Logs d'Erreur - Tous Services",
"type": "logs"
}
],
"refresh": "30s",
"schemaVersion": 36,
"style": "dark",
"tags": ["lecoffre", "monitoring"],
"templating": {
"list": []
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "LeCoffre Node - Vue d'ensemble",
"uid": "lecoffre-overview",
"version": 1,
"weekStart": ""
}

View File

@ -0,0 +1,594 @@
{
"annotations": {
"list": []
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": null,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"vis": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 8,
"x": 0,
"y": 0
},
"id": 1,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltip": {
"mode": "single"
}
},
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "sum(rate({container=~\"sdk_.*\"} |= \"message\" [5m])) by (container)",
"queryType": "",
"refId": "A"
}
],
"title": "Messages par Service SDK",
"type": "timeseries"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"vis": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 8,
"x": 8,
"y": 0
},
"id": 2,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltip": {
"mode": "single"
}
},
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "sum(rate({container=\"sdk_relay\"} |= \"transaction\" [5m])) by (container)",
"queryType": "",
"refId": "A"
}
],
"title": "Transactions Relay",
"type": "timeseries"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"vis": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 8,
"x": 16,
"y": 0
},
"id": 3,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltip": {
"mode": "single"
}
},
"title": "Signatures Signer",
"type": "timeseries"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 8
},
"id": 4,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "count_over_time({container=\"sdk_relay\"} |= \"ERROR\" [1h])",
"queryType": "",
"refId": "A"
}
],
"title": "Erreurs Relay (1h)",
"type": "stat"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 8
},
"id": 5,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.0.0",
"title": "Erreurs Signer (1h)",
"type": "stat"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 6,
"x": 12,
"y": 8
},
"id": 6,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "count_over_time({container=\"sdk_storage\"} |= \"ERROR\" [1h])",
"queryType": "",
"refId": "A"
}
],
"title": "Erreurs Storage (1h)",
"type": "stat"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 6,
"x": 18,
"y": 8
},
"id": 7,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "sum(count_over_time({container=~\"sdk_.*\"} [1h]))",
"queryType": "",
"refId": "A"
}
],
"title": "Total Logs SDK (1h)",
"type": "stat"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"custom": {
"align": "auto",
"cellOptions": {
"type": "auto"
},
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 24,
"x": 0,
"y": 12
},
"id": 8,
"options": {
"cellHeight": "sm",
"footer": {
"countRows": false,
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "{container=~\"sdk_.*\"} |= \"ERROR\" | line_format \"{{.timestamp}} - {{.container}} - {{.message}}\"",
"queryType": "",
"refId": "A"
}
],
"title": "Erreurs Récentes SDK",
"type": "table"
}
],
"refresh": "5s",
"schemaVersion": 37,
"style": "dark",
"tags": [
"sdk",
"relay",
"signer",
"storage"
],
"templating": {
"list": []
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "SDK Services - Monitoring",
"uid": "sdk-services",
"version": 1,
"weekStart": ""
}

View File

@ -0,0 +1,418 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": null,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"vis": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 6,
"x": 0,
"y": 0
},
"id": 1,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"title": "LeCoffre Backend - Volume Logs",
"type": "timeseries"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"vis": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 6,
"x": 6,
"y": 0
},
"id": 2,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "sum by (service) (count_over_time({job=\"lecoffre-front\"} [5m]))",
"queryType": "",
"refId": "A"
}
],
"title": "LeCoffre Frontend - Volume Logs",
"type": "timeseries"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"vis": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 6,
"x": 12,
"y": 0
},
"id": 3,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "sum by (service) (count_over_time({job=\"ihm_client\"} [5m]))",
"queryType": "",
"refId": "A"
}
],
"title": "IHM Client - Volume Logs",
"type": "timeseries"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"vis": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 6,
"x": 18,
"y": 0
},
"id": 4,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"editorMode": "code",
"expr": "sum by (service) (count_over_time({job=\"sdk_relay\"} [5m]))",
"queryType": "",
"refId": "A"
}
],
"title": "SDK Relay - Volume Logs",
"type": "timeseries"
},
{
"datasource": {
"type": "loki",
"uid": "loki"
},
"gridPos": {
"h": 12,
"w": 24,
"x": 0,
"y": 8
},
"id": 5,
"options": {
"showTime": false,
"showLabels": false,
"showCommonLabels": false,
"wrapLogMessage": false,
"prettifyLogMessage": false,
"enableLogDetails": true,
"dedupStrategy": "none",
"sortOrder": "Descending"
},
"title": "Logs d'Erreur - Services Applications",
"type": "logs"
}
],
"refresh": "30s",
"schemaVersion": 36,
"style": "dark",
"tags": ["services", "applications"],
"templating": {
"list": []
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Services Applications - Monitoring",
"uid": "services-overview",
"version": 1,
"weekStart": ""
}

View File

@ -0,0 +1,57 @@
# Configuration Grafana avancée pour LeCoffre Node
[server]
# URL publique de Grafana
root_url = $GRAFANA_URL
# Configuration de sécurité
enable_gzip = true
cert_file =
cert_key =
enforce_domain = false
[security]
# Configuration de sécurité
admin_user = admin
admin_password = admin123
secret_key = lecoffre_grafana_secret_key_2025
# Configuration des sessions
cookie_secure = true
cookie_samesite = strict
[users]
# Configuration des utilisateurs
allow_sign_up = false
allow_org_create = false
auto_assign_org = true
auto_assign_org_id = 1
auto_assign_org_role = Viewer
[auth.anonymous]
# Accès anonyme désactivé pour la sécurité
enabled = false
[dashboards]
# Configuration des dashboards
default_home_dashboard_path = $GRAFANA_CONF_DIR/dashboards/lecoffre-overview.json
[unified_alerting]
# Configuration des alertes unifiées
enabled = true
[log]
# Configuration des logs Grafana
mode = console
level = info
format = json
[metrics]
# Métriques Prometheus
enabled = true
basic_auth_username =
basic_auth_password =
[feature_toggles]
# Fonctionnalités activées
enable = traceqlEditor

View File

View File

View File

View File

View File

View File

@ -0,0 +1,13 @@
$BITCOIN_LOGS_DIR/*.log {
daily
missingok
rotate 7
compress
delaycompress
notifempty
create 644 root root
postrotate
# Redémarrer le service si nécessaire
docker restart bitcoin 2>/dev/null || true
endscript
}

View File

@ -0,0 +1,13 @@
$BLINDBIT_LOGS_DIR/*.log {
daily
missingok
rotate 7
compress
delaycompress
notifempty
create 644 root root
postrotate
# Redémarrer le service si nécessaire
docker restart blindbit 2>/dev/null || true
endscript
}

View File

@ -0,0 +1,13 @@
$IHM_CLIENT_LOGS_DIR/*.log {
daily
missingok
rotate 7
compress
delaycompress
notifempty
create 644 root root
postrotate
# Redémarrer le service si nécessaire
docker restart ihm_client 2>/dev/null || true
endscript
}

View File

@ -0,0 +1,13 @@
$MINER_LOGS_DIR/*.log {
daily
missingok
rotate 7
compress
delaycompress
notifempty
create 644 root root
postrotate
# Redémarrer le service si nécessaire
docker restart lecoffre-front 2>/dev/null || true
endscript
}

View File

@ -0,0 +1,13 @@
$ROOT_DIR_LOGS_MINER/*.log {
daily
missingok
rotate 7
compress
delaycompress
notifempty
create 644 root root
postrotate
# Redémarrer le service si nécessaire
docker restart miner 2>/dev/null || true
endscript
}

View File

@ -0,0 +1,13 @@
$NGINX_LOGS_DIR/*.log {
daily
missingok
rotate 7
compress
delaycompress
notifempty
create 644 root root
postrotate
# Redémarrer le service si nécessaire
docker restart nginx 2>/dev/null || true
endscript
}

View File

@ -0,0 +1,13 @@
$SDK_RELAY_LOGS_DIR/*.log {
daily
missingok
rotate 7
compress
delaycompress
notifempty
create 644 root root
postrotate
# Redémarrer le service si nécessaire
docker restart sdk_relay 2>/dev/null || true
endscript
}

View File

@ -0,0 +1,13 @@
$SDK_STORAGE_LOGS_DIR/*.log {
daily
missingok
rotate 7
compress
delaycompress
notifempty
create 644 root root
postrotate
# Redémarrer le service si nécessaire
docker restart sdk_storage 2>/dev/null || true
endscript
}

View File

@ -0,0 +1,13 @@
$TOR_LOGS_DIR/*.log {
daily
missingok
rotate 7
compress
delaycompress
notifempty
create 644 root root
postrotate
# Redémarrer le service si nécessaire
docker restart tor 2>/dev/null || true
endscript
}

View File

View File

View File

@ -0,0 +1,76 @@
auth_enabled: false
server:
http_listen_port: $LOKI_PORT
grpc_listen_port: 9096
http_listen_address: 0.0.0.0
grpc_listen_address: 0.0.0.0
common:
instance_addr: 0.0.0.0
path_prefix: /loki
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory
schema_config:
configs:
- from: 2020-10-24
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
ruler:
alertmanager_url: http://localhost:9093
# Configuration de l'ingester - SEULEMENT le paramètre crucial
ingester:
lifecycler:
min_ready_duration: 5s # Réduit le délai de 15s à 5s
# Configuration des limites
limits_config:
reject_old_samples: true
reject_old_samples_max_age: 168h
max_cache_freshness_per_query: 10m
split_queries_by_interval: 15m
max_query_parallelism: 32
max_streams_per_user: 0
max_line_size: 256000
ingestion_rate_mb: 16
ingestion_burst_size_mb: 32
per_stream_rate_limit: 3MB
per_stream_rate_limit_burst: 15MB
max_entries_limit_per_query: 5000
max_query_series: 500
max_query_length: 721h
cardinality_limit: 100000
max_streams_matchers_per_query: 1000
max_concurrent_tail_requests: 10
# Configuration du storage
storage_config:
tsdb_shipper:
active_index_directory: /loki/tsdb-index
cache_location: /loki/tsdb-cache
filesystem:
directory: /loki/chunks
# Configuration du compactor
compactor:
working_directory: /loki/compactor
compaction_interval: 10m
retention_enabled: false
delete_request_store: filesystem
# Analytics désactivés
analytics:
reporting_enabled: false

View File

View File

View File

View File

@ -0,0 +1,107 @@
server:
http_listen_port: $PROMTAIL_PORT
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: $LOKI_URL/loki/api/v1/push
scrape_configs:
# Bitcoin Signet Logs
- job_name: bitcoin
static_configs:
- targets:
- localhost
labels:
job: bitcoin
service: bitcoin-signet
__path__: $BITCOIN_LOGS_DIR/*.log
# Blindbit Oracle Logs
- job_name: blindbit
static_configs:
- targets:
- localhost
labels:
job: blindbit
service: blindbit-oracle
__path__:$BLINDBIT_LOGS_DIR/*.log
# SDK Relay Logs
- job_name: sdk_relay
static_configs:
- targets:
- localhost
labels:
job: sdk_relay
service: sdk_relay
__path__:$SDK_RELAY_LOGS_DIR/*.log
# SDK Storage Logs
- job_name: sdk_storage
static_configs:
- targets:
- localhost
labels:
job: sdk_storage
service: sdk_storage
__path__: $SDK_STORAGE_LOGS_DIR/*.log
# LeCoffre Frontend Logs
- job_name: lecoffre-front
static_configs:
- targets:
- localhost
labels:
job: lecoffre-front
service: lecoffre-front
__path__: $LECOFFRE_FRONT_LOGS_DIR/*.log
# IHM Client Logs
- job_name: ihm_client
static_configs:
- targets:
- localhost
labels:
job: ihm_client
service: ihm_client
__path__: $IHM_CLIENT_LOGS_DIR/*.log
# Miner Logs
- job_name: miner
static_configs:
- targets:
- localhost
labels:
job: miner
service: signet_miner
__path__:$MINER_LOGS_DIR/*.log
# Tor Logs
- job_name: tor
static_configs:
- targets:
- localhost
labels:
job: tor
service: tor-proxy
__path__: $TOR_LOGS_DIR/*.log
# Docker Container Logs
- job_name: docker
docker_sd_configs:
- host: unix:///var/run/docker.sock
refresh_interval: 5s
filters:
- name: label
values: ["com.centurylinklabs.watchtower.enable=true"]
relabel_configs:
- source_labels: ['__meta_docker_container_name']
regex: '/?(.*)'
target_label: 'container_name'
- source_labels: ['__meta_docker_container_log_stream']
target_label: 'logstream'
- source_labels: ['__meta_docker_container_label_logging_job_name']
target_label: 'job'

View File

View File

@ -0,0 +1,11 @@
core_url=$BITCOIN_RPBC_URL
ws_url=0.0.0.0:$SDK_RELAY_PORT
wallet_name=$BITCOIN_WALLET_NAME
network=signet
blindbit_url=$BLINDBIT_URL
zmq_url=$ZMQ_URL
storage=$STORAGE_URL
data_dir=$SDK_RELAY_DATA_DIR
bitcoin_data_dir=$BITCOIN_DATA_DIR
bootstrap_url=$RELAY_BOOSTRAP_URL
bootstrap_faucet=true

View File

View File

View File

View File

@ -0,0 +1,51 @@
[supervisord]
nodaemon=true
user=root
logfile=/var/log/supervisor/supervisord.log
pidfile=/var/run/supervisord.pid
childlogdir=/var/log/supervisor
[unix_http_server]
file=/var/run/supervisor.sock
chmod=0700
[supervisorctl]
serverurl=unix:///var/run/supervisor.sock
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[program:nginx]
command=/usr/sbin/nginx -g "daemon off;"
autostart=true
autorestart=true
stderr_logfile=/var/log/supervisor/nginx.err.log
stdout_logfile=/var/log/supervisor/nginx.out.log
user=root
[program:docker-compose]
command=/app/scripts/startup.sh
directory=/app
autostart=true
autorestart=true
stderr_logfile=/var/log/supervisor/docker-compose.err.log
stdout_logfile=/var/log/supervisor/docker-compose.out.log
user=appuser
environment=HOME="/app"
[program:cron]
command=/usr/sbin/cron -f
autostart=true
autorestart=true
stderr_logfile=/var/log/supervisor/cron.err.log
stdout_logfile=/var/log/supervisor/cron.out.log
user=root
[program:logrotate]
command=/usr/sbin/logrotate /etc/logrotate.d/lecoffre
autostart=true
autorestart=false
startsecs=0
exitcodes=0
user=root

0
storage/dev/tor/.gitkeep Normal file
View File

21
storage/dev/tor/torrc Normal file
View File

@ -0,0 +1,21 @@
# Configuration Tor pour LeCoffre Node
# Écoute sur 127.0.0.1 pour la sécurité
# Port SOCKS pour les connexions sortantes
SOCKSPort 127.0.0.1:9050
# Port de contrôle (désactivé pour la sécurité)
# ControlPort 127.0.0.1:$TOR_PORT
# Configuration de base
Log notice file $TOR_LOGS_DIR/tor.log
DataDirectory $SDK_TOR_DATA_DIR
# Configuration réseau
ClientOnly 1
SafeLogging 1
WarnUnsafeSocks 1
# Désactiver les services cachés
HiddenServiceDir $SDK_TOR_DATA_DIR/hidden_service/
HiddenServicePort 80 127.0.0.1:80

161
test_api.py Executable file
View File

@ -0,0 +1,161 @@
#!/usr/bin/env python3
"""
Script de test pour l'API Vault
Teste les endpoints et le chiffrement quantique résistant
"""
import requests
import ssl
import json
from pathlib import Path
# Configuration
API_BASE_URL = "https://vault.4nkweb.com:6666"
VERIFY_SSL = False # Désactivé pour les certificats auto-signés
def test_health_endpoint():
"""Test du endpoint de santé"""
print("🔍 Test du endpoint /health...")
try:
response = requests.get(f"{API_BASE_URL}/health", verify=VERIFY_SSL)
if response.status_code == 200:
data = response.json()
print(f"✅ Santé: {data['status']}")
print(f"🔐 Chiffrement: {data['encryption']}")
print(f"🔧 Algorithme: {data['algorithm']}")
return True
else:
print(f"❌ Erreur santé: {response.status_code}")
return False
except Exception as e:
print(f"❌ Erreur connexion santé: {e}")
return False
def test_info_endpoint():
"""Test du endpoint d'information"""
print("\n🔍 Test du endpoint /info...")
try:
response = requests.get(f"{API_BASE_URL}/info", verify=VERIFY_SSL)
if response.status_code == 200:
data = response.json()
print(f"✅ API: {data['name']} v{data['version']}")
print(f"🌐 Domaine: {data['domain']}")
print(f"🔌 Port: {data['port']}")
print(f"📡 Protocole: {data['protocol']}")
print("📋 Endpoints:")
for endpoint, description in data['endpoints'].items():
print(f" {endpoint}: {description}")
return True
else:
print(f"❌ Erreur info: {response.status_code}")
return False
except Exception as e:
print(f"❌ Erreur connexion info: {e}")
return False
def test_file_endpoint():
"""Test du endpoint de fichier"""
print("\n🔍 Test du endpoint de fichier...")
# Test avec un fichier existant
test_files = [
"dev/bitcoin/bitcoin.conf",
"dev/tor/torrc",
"dev/sdk_relay/sdk_relay.conf"
]
for file_path in test_files:
print(f"\n📁 Test du fichier: {file_path}")
try:
response = requests.get(f"{API_BASE_URL}/{file_path}", verify=VERIFY_SSL)
if response.status_code == 200:
print(f"✅ Fichier servi avec succès")
print(f"📦 Taille: {len(response.content)} bytes")
print(f"🔐 Type chiffrement: {response.headers.get('X-Encryption-Type', 'N/A')}")
print(f"🔧 Algorithme: {response.headers.get('X-Algorithm', 'N/A')}")
# Affichage d'un échantillon du contenu chiffré (base64)
sample = response.content[:100]
print(f"📄 Échantillon chiffré: {sample.decode('utf-8', errors='ignore')}...")
elif response.status_code == 404:
print(f"⚠️ Fichier non trouvé: {file_path}")
else:
print(f"❌ Erreur fichier: {response.status_code}")
if response.headers.get('content-type') == 'application/json':
error_data = response.json()
print(f" Détail: {error_data.get('error', 'N/A')}")
except Exception as e:
print(f"❌ Erreur connexion fichier: {e}")
def test_variable_processing():
"""Test du traitement des variables d'environnement"""
print("\n🔍 Test du traitement des variables...")
# Création d'un fichier de test avec des variables
test_content = """
# Test de variables composites
API_URL=${ROOT_URL}${URL_ROUTE_LECOFFRE_FRONT}
BITCOIN_RPC=${BITCOIN_RPC_URL}
DOMAIN=${DOMAIN}
HOST=${HOST}
COMPOSITE=${ROOT_DIR_LOGS}/bitcoin
"""
test_file_path = Path("/home/debian/4NK_vault/storage/dev/test_variables.conf")
try:
# Création du fichier de test
with open(test_file_path, 'w') as f:
f.write(test_content)
print(f"📝 Fichier de test créé: {test_file_path}")
# Test de l'API
response = requests.get(f"{API_BASE_URL}/dev/test_variables.conf", verify=VERIFY_SSL)
if response.status_code == 200:
print("✅ Variables traitées avec succès")
print(f"📦 Contenu chiffré reçu: {len(response.content)} bytes")
else:
print(f"❌ Erreur traitement variables: {response.status_code}")
# Nettoyage
test_file_path.unlink()
print("🧹 Fichier de test supprimé")
except Exception as e:
print(f"❌ Erreur test variables: {e}")
if test_file_path.exists():
test_file_path.unlink()
def main():
"""Fonction principale de test"""
print("🚀 Test de l'API Vault 4NK")
print("=" * 50)
# Désactivation des avertissements SSL pour les certificats auto-signés
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# Tests
health_ok = test_health_endpoint()
info_ok = test_info_endpoint()
test_file_endpoint()
test_variable_processing()
print("\n" + "=" * 50)
print("📊 Résumé des tests:")
print(f" Santé: {'' if health_ok else ''}")
print(f" Info: {'' if info_ok else ''}")
print(" Fichiers: Voir détails ci-dessus")
print(" Variables: Voir détails ci-dessus")
if health_ok and info_ok:
print("\n🎉 L'API fonctionne correctement!")
else:
print("\n⚠️ L'API a des problèmes")
if __name__ == "__main__":
main()