zcatp2p/API.md
reugenio 7e5b16ee15 Inicial: biblioteca zcatp2p para comunicación P2P segura
- Especificación completa del protocolo (PROTOCOL.md)
- Referencia de API (API.md)
- Implementación crypto: SHA256, ChaCha20-Poly1305
- Device ID con Base32 y verificación Luhn32
- Framing de mensajes (HELLO, PING, DATA, etc.)
- Discovery local UDP broadcast
- Estructura de conexiones y estados
- Build system para Zig 0.15.2

Pendiente: TLS 1.3, STUN, Global Discovery HTTPS, Relay

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 01:06:30 +01:00

476 lines
14 KiB
Markdown

# zcatp2p - API Reference
## 1. Inicialización y Configuración
### 1.1 Tipos Principales
```zig
/// Identificador único de dispositivo (32 bytes)
pub const DeviceId = [32]u8;
/// Representación corta del Device ID (primeros 8 bytes)
pub const ShortId = u64;
/// Configuración del nodo P2P
pub const Config = struct {
/// Nombre del dispositivo (mostrado a otros peers)
device_name: []const u8 = "zcatp2p",
/// Puerto para conexiones entrantes (0 = aleatorio)
listen_port: u16 = 22000,
/// Habilitar discovery local (LAN)
local_discovery: bool = true,
/// Servidores de discovery global (vacío = deshabilitado)
global_discovery_servers: []const []const u8 = &.{},
/// Servidores STUN para NAT traversal
stun_servers: []const []const u8 = &.{
"stun.l.google.com:19302",
"stun.syncthing.net:3478",
},
/// Servidores relay (vacío = deshabilitado)
relay_servers: []const []const u8 = &.{},
/// Directorio para almacenar certificado y configuración
data_dir: []const u8,
/// Compresión LZ4 habilitada
compression: bool = true,
/// Callback cuando se descubre un nuevo dispositivo
on_device_discovered: ?*const fn(DeviceId, []const []const u8) void = null,
/// Callback cuando se recibe un mensaje
on_message_received: ?*const fn(*Connection, Message) void = null,
/// Callback cuando cambia el estado de conexión
on_connection_state_changed: ?*const fn(*Connection, ConnectionState) void = null,
};
/// Estado de una conexión
pub const ConnectionState = enum {
connecting,
connected,
disconnecting,
disconnected,
error,
};
/// Mensaje recibido
pub const Message = struct {
id: u32,
content_type: []const u8,
data: []const u8,
timestamp: i64,
};
/// Información sobre un peer conectado
pub const PeerInfo = struct {
device_id: DeviceId,
device_name: []const u8,
client_name: []const u8,
client_version: []const u8,
addresses: []const []const u8,
connected_at: i64,
is_local: bool,
bytes_sent: u64,
bytes_received: u64,
};
/// Error codes
pub const Error = error{
AlreadyInitialized,
NotInitialized,
InvalidDeviceId,
ConnectionFailed,
ConnectionTimeout,
ConnectionClosed,
PeerNotFound,
CertificateError,
TlsError,
ProtocolError,
CompressionError,
OutOfMemory,
IoError,
InvalidConfig,
};
```
### 1.2 Instancia P2P
```zig
pub const P2P = struct {
/// Inicializa el sistema P2P
/// - Carga o genera certificado
/// - Inicia listeners
/// - Inicia discovery
pub fn init(allocator: std.mem.Allocator, config: Config) Error!*P2P;
/// Libera todos los recursos
pub fn deinit(self: *P2P) void;
/// Obtiene el Device ID local
pub fn getDeviceId(self: *P2P) DeviceId;
/// Obtiene el Device ID como string (formato XXXXXXX-XXXXXXX-...)
pub fn getDeviceIdString(self: *P2P, buf: []u8) []const u8;
/// Parsea un Device ID desde string
pub fn parseDeviceId(str: []const u8) Error!DeviceId;
/// Compara dos Device IDs
pub fn deviceIdEquals(a: DeviceId, b: DeviceId) bool;
/// Obtiene el Short ID (para logging)
pub fn getShortId(device_id: DeviceId) ShortId;
};
```
## 2. Gestión de Conexiones
```zig
pub const P2P = struct {
// ... (continuación)
/// Conecta a un dispositivo por su Device ID
/// Busca automáticamente la dirección usando discovery
pub fn connect(self: *P2P, device_id: DeviceId) Error!*Connection;
/// Conecta a una dirección específica
pub fn connectAddress(self: *P2P, address: []const u8) Error!*Connection;
/// Desconecta de un peer
pub fn disconnect(self: *P2P, device_id: DeviceId) void;
/// Obtiene una conexión existente
pub fn getConnection(self: *P2P, device_id: DeviceId) ?*Connection;
/// Lista todas las conexiones activas
pub fn getConnections(self: *P2P, buf: []*Connection) []const *Connection;
/// Número de conexiones activas
pub fn connectionCount(self: *P2P) usize;
/// Información de un peer
pub fn getPeerInfo(self: *P2P, device_id: DeviceId) ?PeerInfo;
};
pub const Connection = struct {
/// Device ID del peer
pub fn getDeviceId(self: *Connection) DeviceId;
/// Estado actual de la conexión
pub fn getState(self: *Connection) ConnectionState;
/// Información del peer
pub fn getPeerInfo(self: *Connection) PeerInfo;
/// Envía datos al peer
/// Retorna el message_id asignado
pub fn send(
self: *Connection,
content_type: []const u8,
data: []const u8,
) Error!u32;
/// Envía datos y espera confirmación
pub fn sendAndWait(
self: *Connection,
content_type: []const u8,
data: []const u8,
timeout_ms: u32,
) Error!void;
/// Cierra la conexión
pub fn close(self: *Connection) void;
/// Cierra con motivo
pub fn closeWithReason(self: *Connection, reason: []const u8) void;
/// Espera hasta que la conexión esté establecida
pub fn waitConnected(self: *Connection, timeout_ms: u32) Error!void;
/// Verifica si la conexión está activa
pub fn isConnected(self: *Connection) bool;
};
```
## 3. Discovery
```zig
pub const P2P = struct {
// ... (continuación)
/// Busca un dispositivo por su ID
/// Retorna lista de direcciones conocidas
pub fn lookup(
self: *P2P,
device_id: DeviceId,
buf: [][]const u8,
) Error![]const []const u8;
/// Fuerza un anuncio local inmediato
pub fn announceLocal(self: *P2P) void;
/// Fuerza un anuncio global inmediato
pub fn announceGlobal(self: *P2P) Error!void;
/// Obtiene dispositivos descubiertos en la LAN
pub fn getLocalDevices(
self: *P2P,
buf: []DeviceId,
) []const DeviceId;
/// Añade manualmente una dirección conocida para un dispositivo
pub fn addKnownAddress(
self: *P2P,
device_id: DeviceId,
address: []const u8,
) void;
/// Elimina direcciones conocidas de un dispositivo
pub fn clearKnownAddresses(self: *P2P, device_id: DeviceId) void;
};
```
## 4. NAT y Relay
```zig
pub const P2P = struct {
// ... (continuación)
/// Obtiene la IP externa descubierta por STUN
pub fn getExternalAddress(self: *P2P) ?[]const u8;
/// Obtiene el tipo de NAT detectado
pub fn getNatType(self: *P2P) NatType;
/// Verifica si estamos conectados a algún relay
pub fn isRelayConnected(self: *P2P) bool;
/// Obtiene la dirección de relay actual
pub fn getRelayAddress(self: *P2P) ?[]const u8;
};
pub const NatType = enum {
unknown,
none, // Sin NAT (IP pública)
full_cone, // Hole-punchable
restricted, // Hole-punchable
port_restricted,// Hole-punchable
symmetric, // No hole-punchable, necesita relay
blocked, // Sin conectividad UDP
};
```
## 5. Callbacks y Eventos
```zig
/// Callback type para dispositivo descubierto
pub const OnDeviceDiscovered = *const fn(
device_id: DeviceId,
addresses: []const []const u8,
) void;
/// Callback type para mensaje recibido
pub const OnMessageReceived = *const fn(
connection: *Connection,
message: Message,
) void;
/// Callback type para cambio de estado de conexión
pub const OnConnectionStateChanged = *const fn(
connection: *Connection,
old_state: ConnectionState,
new_state: ConnectionState,
) void;
/// Callback type para error
pub const OnError = *const fn(
error_code: Error,
message: []const u8,
) void;
pub const P2P = struct {
// ... (continuación)
/// Registra callback para dispositivo descubierto
pub fn setOnDeviceDiscovered(self: *P2P, cb: ?OnDeviceDiscovered) void;
/// Registra callback para mensaje recibido
pub fn setOnMessageReceived(self: *P2P, cb: ?OnMessageReceived) void;
/// Registra callback para cambio de estado
pub fn setOnConnectionStateChanged(self: *P2P, cb: ?OnConnectionStateChanged) void;
/// Registra callback para errores
pub fn setOnError(self: *P2P, cb: ?OnError) void;
};
```
## 6. Utilidades
```zig
pub const utils = struct {
/// Convierte Device ID a string
pub fn deviceIdToString(id: DeviceId, buf: []u8) []const u8;
/// Parsea Device ID desde string
pub fn stringToDeviceId(str: []const u8) Error!DeviceId;
/// Calcula el dígito de verificación Luhn
pub fn luhn32(str: []const u8) u8;
/// Verifica un Device ID string
pub fn verifyDeviceIdString(str: []const u8) bool;
/// Genera un certificado auto-firmado
pub fn generateCertificate(
allocator: std.mem.Allocator,
common_name: []const u8,
) Error!Certificate;
/// Carga un certificado desde archivo
pub fn loadCertificate(path: []const u8) Error!Certificate;
/// Guarda un certificado a archivo
pub fn saveCertificate(cert: Certificate, path: []const u8) Error!void;
};
```
## 7. Ejemplo de Uso Completo
```zig
const std = @import("std");
const p2p = @import("zcatp2p");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Configuración
const config = p2p.Config{
.device_name = "Simifactu-Empresa-A",
.listen_port = 22000,
.local_discovery = true,
.data_dir = "/home/user/.simifactu/p2p",
.on_message_received = handleMessage,
.on_device_discovered = handleDiscovery,
};
// Inicializar
var node = try p2p.P2P.init(allocator, config);
defer node.deinit();
// Mostrar nuestro Device ID
var id_buf: [64]u8 = undefined;
const my_id = node.getDeviceIdString(&id_buf);
std.debug.print("Mi Device ID: {s}\n", .{my_id});
// Conectar a un peer conocido
const peer_id_str = "ABCDEFG-HIJKLMN-OPQRSTU-VWXYZ23-4567ABC-DEFGHIJ-KLMNOPQ-RSTUVWX";
const peer_id = try p2p.P2P.parseDeviceId(peer_id_str);
var conn = try node.connect(peer_id);
try conn.waitConnected(30000); // 30 segundos timeout
// Enviar una factura
const factura_data = @embedFile("factura.xml");
const msg_id = try conn.send("application/x-simifactu-invoice", factura_data);
std.debug.print("Factura enviada, message_id: {}\n", .{msg_id});
// Esperar respuesta...
// (En producción usaríamos el callback on_message_received)
// Cerrar
conn.close();
}
fn handleMessage(conn: *p2p.Connection, msg: p2p.Message) void {
std.debug.print("Mensaje recibido de {}: type={s}, size={}\n", .{
p2p.P2P.getShortId(conn.getDeviceId()),
msg.content_type,
msg.data.len,
});
// Procesar según content_type
if (std.mem.eql(u8, msg.content_type, "application/x-simifactu-invoice")) {
// Procesar factura recibida
processInvoice(msg.data);
}
}
fn handleDiscovery(device_id: p2p.DeviceId, addresses: []const []const u8) void {
std.debug.print("Dispositivo descubierto: {} en {} direcciones\n", .{
p2p.P2P.getShortId(device_id),
addresses.len,
});
}
fn processInvoice(data: []const u8) void {
// ... procesar factura
_ = data;
}
```
## 8. Integración con Simifactu
### 8.1 Content Types Recomendados
```zig
pub const ContentTypes = struct {
pub const invoice = "application/x-simifactu-invoice";
pub const invoice_ack = "application/x-simifactu-invoice-ack";
pub const certificate = "application/x-simifactu-certificate";
pub const verifactu = "application/x-simifactu-verifactu";
pub const query = "application/x-simifactu-query";
pub const response = "application/x-simifactu-response";
};
```
### 8.2 Flujo Típico
```
Empresa A Empresa B
│ │
│ 1. connect(device_id_B) │
│────────────────────────────────────────────>│
│ │
│ 2. HELLO exchange │
│<═══════════════════════════════════════════>│
│ │
│ 3. send(invoice, factura_xml) │
│────────────────────────────────────────────>│
│ │
│ 4. DATA_ACK │
│<────────────────────────────────────────────│
│ │
│ 5. send(invoice_ack, confirmacion) │
│<────────────────────────────────────────────│
│ │
│ 6. DATA_ACK │
│────────────────────────────────────────────>│
│ │
│ 7. close() │
│────────────────────────────────────────────>│
│ │
```
## 9. Thread Safety
- `P2P.init()` y `P2P.deinit()` deben llamarse desde el mismo thread
- Todas las demás funciones son thread-safe
- Los callbacks pueden ser llamados desde cualquier thread
- Se recomienda no hacer operaciones bloqueantes en callbacks
## 10. Gestión de Memoria
- `P2P.init()` asigna memoria usando el allocator proporcionado
- `P2P.deinit()` libera toda la memoria
- Los strings retornados son válidos hasta la siguiente llamada
- Los mensajes recibidos en callbacks son válidos solo durante el callback
- Para retener datos, copiarlos a memoria propia