# 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